/* ── Roboto (self-hosted) — matches the mobile app's default Android font ── */
@font-face {
    font-family: 'Roboto';
    font-style: normal;
    font-weight: 400;
    font-display: swap;
    src: url('../ASSETS/fonts/roboto-400.woff2') format('woff2');
}
@font-face {
    font-family: 'Roboto';
    font-style: normal;
    font-weight: 500;
    font-display: swap;
    src: url('../ASSETS/fonts/roboto-500.woff2') format('woff2');
}
@font-face {
    font-family: 'Roboto';
    font-style: normal;
    font-weight: 700;
    font-display: swap;
    src: url('../ASSETS/fonts/roboto-700.woff2') format('woff2');
}

/* ── Buttons use the page font ─────────────────────────────────────────────────
   Form controls do not inherit font-family — the UA gives them the system font,
   which has no 500 face, so font-weight 300/400/500 all render identically on
   them. This hands every button the body stack (Roboto first, with the system
   fonts still behind it as fallback for glyphs Roboto lacks, e.g. ✕).
   Element selector, so any rule naming a font-family — the Courier "i" badges,
   for instance — still wins. */
   button { font-family: inherit; }

@keyframes hitPulseGround {
    0%,to {
        transform: translateY(0) scale(1)
    }

    50% {
        transform: translateY(0) var(--hit-scale, scale(1.05))
    }
}

@keyframes hitPulseHover {
    0%,to {
        transform: translateY(-15px) scale(1)
    }

    50% {
        transform: translateY(-15px) var(--hit-scale, scale(1.05))
    }
}

@keyframes slideWobbleGround {
    0%,to {
        transform: translateY(0) rotate(0)
    }

    25% {
        transform: translateY(0) rotate(-2deg)
    }

    75% {
        transform: translateY(0) rotate(2deg)
    }
}

@keyframes slideWobbleHover {
    0%,to {
        transform: translateY(-15px) rotate(0)
    }

    25% {
        transform: translateY(-15px) rotate(-2deg)
    }

    75% {
        transform: translateY(-15px) rotate(2deg)
    }
}

@keyframes hitGlowFade {
    0%,to {
        border-color: var(--border-accent-light);
        box-shadow: none
    }

    28.6% {
        border-color: var(--color-accent)!important;
        box-shadow: 0 0 12px var(--glow-accent-strong),inset 0 0 8px var(--border-accent-light)
    }
}

@keyframes hitGlowFadeIn {
    0% {
        border-color: var(--border-accent-light);
        box-shadow: none
    }

    to {
        border-color: var(--color-accent)!important;
        box-shadow: 0 0 12px var(--glow-accent-strong),inset 0 0 8px var(--border-accent-light)
    }
}

@keyframes slideShimmer {
    0%,to {
        border-color: var(--glow-accent-strong);
        box-shadow: 0 0 12px rgba(255,216,75,.36),inset 0 0 8px rgba(255,216,75,.12)
    }

    50% {
        border-color: var(--color-accent)!important;
        box-shadow: 0 0 12px var(--glow-accent-strong),inset 0 0 8px var(--border-accent-light)
    }
}

@keyframes moduleShowAnimation {
    0% {
        opacity: 0;
        transform: translateY(-20px);
        max-height: 0
    }

    to {
        opacity: 1;
        transform: translateY(0);
        max-height: 10000px
    }
}

@keyframes moduleHideAnimation {
    0% {
        opacity: 1;
        transform: translateY(0);
        max-height: 10000px
    }

    to {
        opacity: 0;
        transform: translateY(-20px);
        max-height: 0
    }
}

@keyframes sgp-ripple-out {
    to {
        transform: translate(-50%,-50%) scale(16);
        opacity: 0
    }
}

@keyframes sdp-ripple-out {
    to {
        transform: translate(-50%,-50%) scale(14);
        opacity: 0
    }
}

@keyframes hpp-ripple-out {
    to {
        transform: translate(-50%,-50%) scale(14);
        opacity: 0
    }
}

@keyframes hpp-rec-pulse {
    0% {
        box-shadow: 0 0 4px rgba(220,60,60,.4)
    }

    to {
        box-shadow: 0 0 12px rgba(220,60,60,.9)
    }
}

@keyframes hpp-countdown-pop {
    0% {
        transform: scale(1.4);
        opacity: 0
    }

    to {
        transform: scale(1);
        opacity: 1
    }
}

:root {
    /* Dark form-control internals + native dropdowns match the theme
       (light-theme.css flips this to `light` under html.bws-light) */
    color-scheme: dark;
    --color-background: #1a1a1a;
    --color-surface: #242424;
    --color-button: #2c2c2c;
    --color-border: #404040;
    --color-text: #ffffff;
    --color-text-secondary: rgba(255, 255, 255, 0.7);
    --color-success: #4CAF50;
    --color-danger: #f44336;
    --color-warning: #ff9800;
    --border-radius: 2px;
    --color-accent: #ffd84b;
    --color-accent-dark: #f5b501;
    --bws-header-h: 52px;   /* fixed app header height — offsets body + control bar */
    /* Frosted-glass modals — the scrim behind a modal and the modal panel's own
       fill. Light mode redefines just these two; see "Frosted-glass modals". */
    --glass-scrim: rgba(0, 0, 0, 0.72);
    --glass-panel: rgba(20, 20, 20, 0.1);
    --bg-dark-heavy: rgba(0, 0, 0, 0.6);
    --bws-sticky-bg: rgba(18, 18, 18, 0.97);  /* opaque plate under the sticky sequencer controls */
    --bg-dark-medium: rgba(0, 0, 0, 0.4);
    --bg-dark-light: rgba(0, 0, 0, 0.4);
    --bg-dark-lighter: rgba(0, 0, 0, 0.4);
    --bg-accent-heavy: rgba(255, 216, 75, 0.35);
    --bg-accent-medium: rgba(255, 216, 75, 0.2);
    --bg-accent-light: rgba(255, 216, 75, 0.1);
    --border-accent-light: rgba(255, 216, 75, 0.2);
    --border-accent-medium: rgba(255, 216, 75, 0.15);
    --glow-accent: rgba(255, 216, 75, 0.3);
    --glow-accent-medium: rgba(255, 216, 75, 0.25);
    --glow-accent-heavy: rgba(255, 216, 75, 0.5);
    --glow-accent-strong: rgba(255, 216, 75, 0.6);
    --text-muted: rgba(255, 255, 255, 0.6);
    --text-bright: rgba(255, 255, 255, 0.8);
    --text-white: #ffffff;
    --bg-success: rgba(40, 167, 69, 0.35);
    --bg-danger: rgba(220, 53, 69, 0.35);
    --glow-danger: rgba(220, 53, 69, 0.4);
    --input-bg: rgba(17, 17, 17, 0.8);
    --input-bg-hover: rgba(30, 30, 30, 0.9);
    --input-bg-focus: rgba(20, 20, 20, 0.95);
    --breakpoint-xs: 320px;
    --breakpoint-sm: 480px;
    --breakpoint-md: 768px;
    --breakpoint-lg: 1024px;
    --breakpoint-xl: 1280px
}

html {
    margin: 0;
    padding: 0;
    scrollbar-width: thin;
    scrollbar-color: var(--color-accent) #000;
}
html::-webkit-scrollbar { width: 10px; }
html::-webkit-scrollbar-track { background: #000; }
html::-webkit-scrollbar-thumb {
    background: var(--color-accent);
    border-radius: 6px;
    border: 2px solid #000;
    background-clip: padding-box;
}

#frequency-set-1.locked .bbp-spinner-buttons,#frequency-set-2.locked .bbp-spinner-buttons {
    display: none!important;
    pointer-events: none!important
}

.bbp-session-row:last-child .bbp-add-row-between {
    width: 36px;
    height: 36px;
    bottom: -46px
}

.bbp-module-toggle-button {
    width: 17px;
    height: 17px;
    background-color: #fff;
    border-radius: 9px;
    position: absolute;
    transition: all .3s ease;
    left: 2px;
    z-index: 2
}

.bbp-module-toggle.active .bbp-module-toggle-button {
    left: 25px
}

.bbp-spinner-buttons {
    display: flex;
    flex-direction: column;
    gap: 0;
    height: 35px;
    margin-left: 4px
}

.bbp-spinner-button {
    width: 24px;
    height: 50%;
    padding: 0;
    border-radius: 0;
    background-color: var(--bg-accent-light);
    color: var(--color-accent)!important;
    cursor: pointer;
    font-size: 10px;
    font-weight: 400;
    transition: all .2s;
    display: flex;
    align-items: center;
    justify-content: center;
    margin: 0;
    line-height: 1
}

.bbp-spinner-button:hover {
    background-color: var(--border-accent-light)
}

.bbp-spinner-button:active {
    background-color: var(--glow-accent)
}

.bbp-spinner-button,
.bbp-spinner-down,
.bbp-spinner-up {
    border: 1px solid var(--color-accent)
}

.bbp-spinner-up {
    border-bottom: none
}

.bbp-spinner-down {
    border-top: none;
    margin-top: -1px;
    padding-top: 3px;
    box-sizing: border-box
}

.bbp-frequency-input::-webkit-inner-spin-button,
.bbp-frequency-input::-webkit-outer-spin-button {
    display: none
}

.bws-icon-button {
    height: auto;
    min-height: 60px;
    transition: all .2s ease;
    border-radius: 4px;
    border: 1px solid var(--border-accent-light);
    background: var(--bg-dark-medium);
    cursor: pointer;
    padding: .5rem;
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: .25rem;
    font-size: 11px;
    color: var(--text-muted);
    flex: 1;
    min-width: 0
}

.bws-icon-button svg {
    height: 24px;
    width: 90%;
    stroke-width: 2px;
    transition: all .2s ease
}

.bws-icon-button:hover {
    background: var(--bg-accent-light);
    border-color: var(--color-accent)!important;
    cursor: pointer;
    color: var(--text-bright);
    box-shadow: 0 0 6px var(--border-accent-light)
}

.bws-icon-button.active {
    background: var(--border-accent-light);
    border-color: var(--color-accent)!important;
    color: var(--color-accent)!important;
    box-shadow: 0 0 8px var(--glow-accent)
}

.bws-icon-button svg,.bws-icon-button.active svg {
    stroke: var(--color-accent)
}

.bws-button-a, .bws-main-button-style {
    padding: .75rem 2rem;
    border-radius: 4px;
    font-size: 12px;
    font-weight: 400;
    cursor: pointer;
    transition: all .2s ease;
    width: auto;
    white-space: nowrap;
    text-align: center
}

/* Solid accent yellow: a shade down at rest, full #ffd84b on hover. */
.bws-button-a {
    border: 1px solid #d9b53c;
    font-size: 13px;
    min-width: 120px;
    background: #d9b53c;
    color: #111;
    display: flex;
    align-items: center;
    justify-content: center
}

.bws-button-a:hover {
    background: #ffd84b;
    border-color: #ffd84b;
    cursor: pointer;
    color: #111
}

/* Master play buttons are green rather than accent yellow, on the same
   rest/hover pair as the green section buttons (.ft-add-timer-btn and friends):
   #28a745 at rest, #34c759 on hover. Shape, size, glyph and the red .playing
   state below are the shared ones — colour is the only thing that differs.
   White text, as on the green discs; black on green reads far worse than black
   on yellow.
   Only the button that starts the whole module: the three instrument players,
   the three timer sessions, breathwork and tinnitus relief, the Media Mixer's and
   Singing Bowls' Play Session, the visualizer's Start, and the one playlist Play
   the album/library modules share. Two running-state hooks, not one: Singing Bowls swaps on `.running`
   where everything else uses `.playing`. Deliberately NOT here: Singing Bowls'
   "Play Scale" (a demo, not the module's transport) and every per-row/preview
   ▶, which stay yellow. */
#hpp-play:not(.playing),
#sdp-play:not(.playing),
#sgp-play:not(.playing),
#ft-session-play:not(.playing),
#bt-session-play:not(.playing),
#it-session-play:not(.playing),
#brw-session-play:not(.playing),
#tin-session-play:not(.playing),
#av2-start-btn:not(.playing),
#mr-preview-button:not(.playing),
#sbp-auto-session-button:not(.running),
.pl-main-play:not(.playing) {
    border-color: #28a745;
    background: #28a745;
    color: #fff;
}
#hpp-play:not(.playing):hover,
#sdp-play:not(.playing):hover,
#sgp-play:not(.playing):hover,
#ft-session-play:not(.playing):hover,
#bt-session-play:not(.playing):hover,
#it-session-play:not(.playing):hover,
#brw-session-play:not(.playing):hover,
#tin-session-play:not(.playing):hover,
#av2-start-btn:not(.playing):hover,
#mr-preview-button:not(.playing):hover,
#sbp-auto-session-button:not(.running):hover,
.pl-main-play:not(.playing):hover {
    border-color: #34c759;
    background: #34c759;
    color: #fff;
}

/* Stop state: same solid-fill logic as the Play state, in red — a shade down at
   rest, full #dc3545 on hover. White text here, since black on red reads far
   worse than black on yellow. */
.bws-button-a.playing {
    border: 1px solid #b32b38;
    background: #b32b38;
    color: #fff;
    transition: all .2s ease
}

.bws-button-a.playing:hover {
    background: #dc3545;
    border-color: #dc3545;
    color: #fff
}

#hpp-play,
#sdp-play,
#sgp-play,
#bbp-play-button,
#fp-play-button,
#ft-session-play,
#bt-session-play,
#it-session-play,
#brw-session-play,
#tin-session-play,
#av2-start-btn,
#sbp-play-scale-button,
.pl-main-play {
    font-size: 13px;
    gap: 7px
}

/* Wide enough for the longer of the two labels, so the button does not resize
   when Play flips to Pause. The per-bowl buttons are excluded — they sit four
   to a row and size themselves to the column. */
#hpp-play,
#sdp-play,
#sgp-play,
#bbp-play-button,
#fp-play-button,
#ft-session-play,
#bt-session-play,
#it-session-play,
#brw-session-play,
#tin-session-play,
#sbp-play-scale-button,
.pl-main-play {
    min-width: 150px
}

.bws-button-a.playing {
    --bws-transport-glyph: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Crect x='6' y='5' width='4' height='14'/%3E%3Crect x='14' y='5' width='4' height='14'/%3E%3C/svg%3E")
}

#hpp-play::before,
#sdp-play::before,
#sgp-play::before,
#bbp-play-button::before,
#fp-play-button::before,
#ft-session-play::before,
#bt-session-play::before,
#it-session-play::before,
#brw-session-play::before,
#tin-session-play::before,
#av2-start-btn::before,
#sbp-play-scale-button::before,
.pl-main-play::before,
#sbp-auto-session-button .desktop-text::before {
    content: "";
    width: 16px;
    height: 16px;
    flex: 0 0 auto;
    background-color: currentColor;
    -webkit-mask: var(--bws-transport-glyph, url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath d='M8 5v14l11-7z'/%3E%3C/svg%3E")) center/contain no-repeat;
    mask: var(--bws-transport-glyph, url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath d='M8 5v14l11-7z'/%3E%3C/svg%3E")) center/contain no-repeat
}

.bws-main-button-style {
    border: 1px solid var(--color-accent);
    min-width: 140px;
    background: var(--border-accent-light);
    color: var(--color-accent);
    box-shadow: none
}

.bws-main-button-style:hover {
    background: var(--color-accent);
    border-color: var(--color-accent);
    cursor: pointer;
    color: #111;
    box-shadow: none
}

.fsb-row-wave-button {
    cursor: pointer;
    transition: all .2s ease;
    display: flex;
    align-items: center;
    justify-content: center
}

.bbp-row-play-button.playing,.bbp-row-play-button.playing:hover,
.fsb-row-play-button.playing,.fsb-row-play-button.playing:hover {
    background: #dc3545;
    box-shadow: 0 2px 4px rgba(220,53,69,.3)
}

.bbp-row-play-button.playing:hover,
.fsb-row-play-button.playing:hover {
    transform: none
}

.bbp-row-play-button .play-icon,
.fsb-row-play-button .play-icon {
    display: block
}

.bbp-row-play-button .stop-icon,
.fsb-row-play-button .stop-icon {
    display: none;
    position: absolute
}

.bbp-row-play-button.playing .play-icon,
.fsb-row-play-button.playing .play-icon {
    display: none
}

.bbp-row-play-button.playing .stop-icon,
.fsb-row-play-button.playing .stop-icon {
    display: block;
    position: absolute
}

.fsb-row-wave-button {
    width: 38px;
    height: 38px;
    padding: 2px;
    border: 1px solid var(--border-accent-light);
    border-radius: 4px;
    background: var(--bg-dark-light);
    color: var(--text-muted)
}

.fsb-row-wave-button:hover {
    background: var(--bg-accent-light);
    border-color: var(--color-accent)!important;
    color: var(--text-bright);
    box-shadow: 0 0 6px var(--border-accent-light)
}

.bbp-row-wave-button.active,
.fsb-row-wave-button.active {
    background: var(--border-accent-light);
    border-color: var(--color-accent)!important;
    color: var(--color-accent)!important;
    box-shadow: 0 0 8px var(--glow-accent)
}

.sbp-toggle-button svg,
.fsb-row-wave-button svg {
    width: 90%;
    height: 90%;
    stroke: currentColor
}

.bws-x-close-button {
    position: absolute;
    right: 6px;
    top: 6px;
    width: 24px;
    height: 24px;
    background: #dc3545 url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' width='24' height='24'%3E%3Cpath d='M18 6L6 18M6 6l12 12' stroke='%23FFFFFF' stroke-width='2.5' stroke-linecap='round'/%3E%3C/svg%3E")center/14px no-repeat;
    border-radius: 50%;
    border: 0;
    cursor: pointer;
    padding: 0;
    z-index: 2;
    box-shadow: 0 2px 4px rgba(220,53,69,.3);
    transition: all .2s ease
}

.bws-x-close-button:hover {
    background: var(--color-accent)url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' width='24' height='24'%3E%3Cpath d='M18 6L6 18M6 6l12 12' stroke='%23111111' stroke-width='2.5' stroke-linecap='round'/%3E%3C/svg%3E")center/14px no-repeat;
    transform: scale(1.1);
    box-shadow: 0 4px 8px var(--glow-accent)
}

.bws-x-close-button:disabled,
.bws-x-close-button.bws-x-close-disabled {
    opacity: .25;
    cursor: default;
    pointer-events: none
}

.close-container {
    position: absolute;
    border: 0;
    border-radius: 50%;
    cursor: pointer;
    transition: all .2s ease
}

.close-container {
    right: 10px;
    top: 10px;
    display: none
}

.close-container.show {
    display: block
}

.bbp-session-controls .bws-button-a,.sbp-session-controls .bws-button-a,
.fsb-session-controls .bws-button-a,.hpp-player-row .bws-button-a {
    margin-top: 0
}

.bws-button-a-v2 {
    border: 1px solid #dc3545!important;
    background: var(--bg-danger)!important;
    color: #fff!important;
    box-shadow: inset 0 0 10px var(--glow-danger)!important
}

.bbp-frequency-input::-webkit-inner-spin-button,
.bbp-frequency-input::-webkit-outer-spin-button {
    -webkit-appearance: none;
    margin: 0
}

.bws-button-a-v2:hover {
    background: rgba(220,53,69,.7)!important;
    border-color: #dc3545!important;
    box-shadow: inset 0 0 15px var(--glow-danger)!important
}

#sbp-auto-session-button.running:hover {
    background: #dc3545!important;
    border-color: #dc3545!important;
    box-shadow: none!important
}

.bws-button-a-v3 {
    background: var(--border-accent-light)!important;
    color: var(--color-accent)!important;
    border-color: var(--color-accent)!important;
    box-shadow: none!important
}

.bws-button-a-v3:hover {
    background: rgba(255,216,75,.7)!important;
    color: #111!important;
    border-color: var(--color-accent)!important;
    box-shadow: none!important
}

/* Same solid-fill logic as the other transports' paused state. */
#sbp-auto-session-button.running {
    border: 1px solid #b32b38!important;
    background: #b32b38!important;
    color: #fff!important;
    box-shadow: none!important
}

#sbp-auto-session-button.running:hover {
    color: #fff!important
}

/* Keep the session button a fixed width across its Play / Pause / Resume Session labels:
   an invisible sizer reserves the width of the longest label ("Resume Session") while the live
   label is overlaid on top, so the button never resizes as its text changes. */
#sbp-auto-session-button { position: relative; font-size: 13px; }
/* The sizer reserves room for the glyph and its gap as well as the longest label. */
#sbp-auto-session-button .sbp-session-sizer { visibility: hidden; pointer-events: none; padding-left: 23px; }
#sbp-auto-session-button .desktop-text {
    position: absolute;
    inset: 0;
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 7px;
}

/* Its running state is `.running`, not `.playing` — same swap, different hook. */
#sbp-auto-session-button.running {
    --bws-transport-glyph: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Crect x='6' y='5' width='4' height='14'/%3E%3Crect x='14' y='5' width='4' height='14'/%3E%3C/svg%3E")
}

[class*=sbp-][class*=-btn] {
    letter-spacing: -.2px;
    padding: 8px 4px
}

.sbp-bowl-btn.sbp-hit-btn {
    position: relative;
    padding-bottom: 12px!important
}

.sbp-auto-dist-btn,.sbp-auto-pan-btn {
    margin-bottom: 6px
}

.sbp-toggle-button {
    padding: 2px;
    border: 1px solid var(--border-accent-light);
    border-radius: 4px;
    background: 0 0;
    color: var(--color-accent);
    cursor: pointer;
    transition: all .2s ease;
    display: flex;
    align-items: center;
    justify-content: center;
    font-size: 11px;
    min-width: 30px;
    min-height: 30px
}

.sbp-toggle-button:hover {
    border-color: var(--color-accent);
    box-shadow: 0 0 6px var(--border-accent-light)
}

.bowls-buttons-general.active,.sbp-toggle-button.active {
    background: var(--border-accent-light);
    border-color: var(--color-accent)!important;
    color: var(--color-accent)!important;
    box-shadow: 0 0 8px var(--glow-accent)
}

.sbp-bowl-btn {
    position: relative;
    height: 35px;
    padding: 0 .5rem;
    white-space: nowrap;
    width: 100%;
    min-width: auto;
    flex: 1
}

.bowls-buttons-general,.sbp-keyboard-label {
    display: flex;
    align-items: center;
    font-size: 12px
}

.sbp-keyboard-label {
    position: absolute;
    top: 4px;
    right: 3px;
    width: 16px;
    height: 16px;
    background-color: #000;
    border: 1px solid var(--color-accent);
    border-radius: 2px;
    justify-content: center;
    font-weight: 400;
    color: var(--color-accent)
}

.bowls-buttons-general {
    height: auto;
    transition: all .2s ease;
    border-radius: 4px;
    border: 1px solid var(--border-accent-light);
    background: var(--bg-dark-light);
    cursor: pointer;
    padding: .5rem;
    flex-direction: column;
    gap: .25rem;
    color: var(--text-muted)
}

.bbp-frequency-set.locked .bws-dropdown {
    pointer-events: none;
    opacity: .7;
    background: #1a1a1a;
    cursor: not-allowed
}

.bws-dropdown:disabled {
    background: #1a1a1a;
    cursor: not-allowed
}

.bws-dropdown {
    width: 100%;
    height: 38px;
    padding: .625rem 1.75rem .625rem .75rem;
    border: 1px solid var(--color-accent);
    border-radius: 2px;
    font-size: 13px;
    line-height: 1.2;
    background: var(--input-bg)url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23ffd84b' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M6 9l6 6 6-6'%3E%3C/path%3E%3C/svg%3E")no-repeat right .5rem center;
    background-size: .7rem;
    -webkit-appearance: none;
    -moz-appearance: none;
    appearance: none;
    color: var(--color-accent)!important;
    cursor: pointer;
    transition: border-color .2s ease,box-shadow .2s ease,background-color .2s ease;
    font-weight: 500;
    box-shadow: 0 0 10px var(--glow-accent);
    box-sizing: border-box
}

/* ── Custom-dropdown trigger caret ────────────────────────────────────────────
   The custom dropdowns (bowl type, and every trigger initStyledSelect builds)
   are <button>s wearing .bws-dropdown, not real <select>s. They get no
   ::picker-icon, so their caret cannot ride on base-select support the way a
   select's does. Draw it here instead: one small accent caret, identical in
   every browser and every one of these dropdowns. The button's own
   background-image chevron is dropped so there is exactly one.
   A mask rather than a baked-in data-URI colour, so the light theme's darker
   accent applies without a second copy of the glyph. */
button.bws-dropdown {
    position: relative;
    background-image: none;
    padding-right: 1.75rem;
}
button.bws-dropdown::after {
    content: "";
    position: absolute;
    top: 50%;
    right: .6rem;
    width: .7rem;
    height: .7rem;
    transform: translateY(-50%);
    background-color: var(--color-accent);
    -webkit-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M6 9l6 6 6-6'%3E%3C/path%3E%3C/svg%3E") no-repeat center / contain;
    mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M6 9l6 6 6-6'%3E%3C/path%3E%3C/svg%3E") no-repeat center / contain;
    pointer-events: none;
}

/* ── Shared info badge "i" — single source of truth for every module info button ──
   Worn by both the interactive round buttons and the non-interactive badge that
   heads an info modal, so the two always read as the same object. Module rules may
   still override geometry (size, margins, positioning); they must NOT restate the
   colours — those live here and nowhere else. */
.bws-info-badge {
    width: 24px; height: 24px; flex-shrink: 0;
    display: flex; align-items: center; justify-content: center;
    border: 0; border-radius: 50%; line-height: 1;
    background: var(--color-accent, #ffd84b); color: #000;
    font-style: normal;
    font-family: 'Courier New', Courier, monospace;
    font-size: 15px;
    font-weight: 700;
}
/* Clickable ones idle a shade darker and brighten to the accent on hover — the same
   idle/hover pair as .bws-card-create-btn. Static badges keep the flat accent. */
button.bws-info-badge {
    cursor: pointer;
    background: #d9b53c;
    transition: background-color .2s ease;
}
button.bws-info-badge:hover { background: var(--color-accent, #ffd84b); }
button.bws-info-badge:disabled { opacity: 0.35; cursor: not-allowed; }

/* Same guard for the classic (non-base-select) path. Scoped to `select` because
   the <button> triggers wearing .bws-dropdown do their own ellipsis on an inner
   span, and overflow:hidden here would clip their absolutely-positioned caret. */
select.bws-dropdown {
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

.bws-dropdown:hover,.sbp-config-section .bws-dropdown:hover {
    border-color: var(--color-accent)!important;
    background-color: var(--input-bg-hover);
    box-shadow: 0 0 15px var(--glow-accent-heavy)
}

.bws-dropdown:focus,.sbp-config-section .bws-dropdown:focus {
    outline: 0;
    border-color: var(--color-accent)!important;
    box-shadow: 0 0 20px var(--glow-accent-strong);
    background-color: var(--input-bg-focus)
}

.bws-dropdown option {
    background: var(--input-bg-focus);
    color: #f8f9fa;
    padding: .75rem;
    border: 0;
    margin: 4px 0
}

.bws-dropdown option:checked {
    background: linear-gradient(var(--color-accent),var(--color-accent));
    background-color: var(--border-accent-light)!important;
    color: var(--color-accent)!important
}

.bws-dropdown optgroup {
    background: var(--input-bg-focus);
    color: var(--color-accent)!important;
    font-weight: 600;
    padding: .5rem 0
}

/* ── Open dropdown ────────────────────────────────────────────────────────────
   The `option` rules above are everything a NATIVE popup accepts: its hover
   highlight is the OS accent (blue on Windows) and its frame is the OS grey, so
   every dropdown in the app stopped looking like the app the moment it opened.
   Customizable-select lets us draw the popup ourselves. Feature-queried, and
   scoped to .bws-dropdown so it lands on every dropdown at once rather than
   making one module's picker a special case: a browser without support skips
   the whole block and keeps the native list exactly as before. */
@supports (appearance: base-select) {
    /* Scoped to `select`: .bws-dropdown is also worn by the <button> triggers of
       the custom dropdowns (bowl type, and every styled select built by
       initStyledSelect). A button can never have a ::picker-icon, so an
       unscoped `background-image: none` below stripped their chevron and gave
       them nothing back — they lost their caret entirely on browsers that
       support base-select. */
    select.bws-dropdown,
    select.bws-dropdown::picker(select) { appearance: base-select; }

    select.bws-dropdown::picker(select) {
        padding: 4px;
        margin-top: 4px;
        border: 1px solid var(--color-accent);
        border-radius: 6px;
        background: var(--input-bg-focus);
        box-shadow: 0 8px 24px rgba(0,0,0,.55), 0 0 12px var(--glow-accent);
    }
    /* The base appearance draws its own arrow, so the background-image chevron
       .bws-dropdown carries would otherwise show up twice. Only a real <select>
       gets that arrow; the button triggers keep the background chevron.

       The closed button is laid out explicitly: the value takes the free space and
       shrinks, the arrow keeps its own. Long values (audio device names run to
       "Communications - Stereo Mix (Realtek High Definition Audio)") would
       otherwise wrap, because under base-select the selected option is laid out
       for real inside the button where a classic select would simply clip. */
    select.bws-dropdown {
        display: flex;
        align-items: center;
        gap: .5rem;
        background-image: none;
        padding-right: .75rem;
    }
    /* ::selectedcontent is the button's copy of the selected option, so truncating
       here leaves the list untouched — the picker still shows every name in full.
       min-width:0 is what makes it shrink at all: flex items default to
       min-width:auto and would shove the arrow off the edge instead. */
    select.bws-dropdown::selectedcontent {
        flex: 1 1 auto;
        min-width: 0;
        overflow: hidden;
        text-overflow: ellipsis;
        white-space: nowrap;
    }
    select.bws-dropdown::picker-icon {
        flex: 0 0 auto;
        color: var(--color-accent);
        transition: transform .2s ease
    }
    select.bws-dropdown:open::picker-icon { transform: rotate(180deg) }

    select.bws-dropdown option {
        margin: 0;
        padding: .5rem .625rem;
        border-radius: 4px;
        background: transparent;
        color: #f8f9fa;
        font-size: 13px;
        cursor: pointer
    }
    /* Replaces the system-blue row highlight. */
    select.bws-dropdown option:hover,
    select.bws-dropdown option:focus {
        background: var(--bg-accent-medium, rgba(255,216,75,.14));
        color: var(--color-accent)
    }
    select.bws-dropdown option:checked {
        background: rgba(255,216,75,.10)!important;
        color: var(--color-accent)!important
    }
    select.bws-dropdown option::checkmark { color: var(--color-accent) }
    select.bws-dropdown optgroup { background: transparent; padding: .5rem .625rem 0 }
}

.sbp-config-input {
    width: 100%;
    padding: .625rem .75rem;
    border: 1px solid var(--color-accent);
    border-radius: 2px;
    font-family: inherit;
    font-size: 13px;
    background: var(--input-bg);
    color: var(--color-accent)!important;
    cursor: text;
    transition: all .2s ease;
    font-weight: 500;
    box-shadow: 0 0 10px var(--glow-accent);
    caret-color: var(--color-accent)!important;
    box-sizing: border-box
}

.sbp-time-select::selection {
    background-color: var(--glow-accent);
    color: var(--color-accent)
}

.sbp-bowl-controls,
.sbp-config-section,
.mr-modules-row {
    display: flex;
    flex-direction: column;
    gap: .5rem
}

.sbp-config-section .bws-dropdown,.sbp-time-select {
    width: 100%;
    border: 1px solid var(--color-accent);
    border-radius: 2px;
    font-family: inherit;
    color: var(--color-accent)!important;
    transition: all .2s ease;
    font-weight: 500;
    box-shadow: 0 0 10px var(--glow-accent);
    -webkit-appearance: none;
    -moz-appearance: none;
    appearance: none;
    box-sizing: border-box
}

.sbp-config-section .bws-dropdown {
    height: 38px;
    padding: .625rem 1.75rem .625rem .75rem;
    font-size: 13px;
    line-height: 1.2;
    background: var(--input-bg)url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23ffd84b' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M6 9l6 6 6-6'%3E%3C/path%3E%3C/svg%3E")no-repeat right .5rem center;
    background-size: .7rem;
    cursor: pointer
}

.sbp-time-select {
    padding: 5px 4px 7px 4px;
    font-size: 16px;
    background: var(--input-bg);
    cursor: text;
    caret-color: var(--color-accent)!important;
    text-align: center
}

.sbp-time-select:hover {
    border-color: var(--color-accent)!important;
    background-color: var(--input-bg-hover);
    box-shadow: 0 0 15px var(--glow-accent-heavy)
}

.sbp-time-select:focus {
    outline: 0;
    border-color: var(--color-accent)!important;
    box-shadow: 0 0 20px var(--glow-accent-strong);
    background-color: var(--input-bg-focus)
}

.sbp-time-select::-webkit-calendar-picker-indicator {
    cursor: pointer;
    filter: invert(1);
    opacity: .8
}

.sbp-time-select:-ms-input-placeholder,.sbp-time-select::-moz-placeholder,.sbp-time-select::-webkit-input-placeholder,.sbp-time-select::placeholder {
    color: var(--color-accent)!important;
    opacity: 1!important
}

#frequency-set-1.locked .bbp-spinner-wrapper,#frequency-set-2.locked .bbp-spinner-wrapper {
    pointer-events: none
}

.bbp-frequency-set.locked .bws-volume-slider {
    pointer-events: auto;
    opacity: 1
}

.bowl-frequency-control .bbp-spinner-wrapper,
.mlib-fbar-rangewrap .mlib-fbar-input {
    flex: 1;
    min-width: 0
}

.bbp-slider-input-group {
    display: flex;
    gap: 1rem;
    align-items: center;
    margin-bottom: 1rem
}

.bws-module-container-main .bws-volume-slider::-webkit-slider-thumb,
.bws-volume-slider::-webkit-slider-thumb {
    -webkit-appearance: none;
    width: 16px;
    height: 16px;
    border-radius: 50%;
    background: var(--color-accent);
    cursor: pointer;
    transition: all .2s;
    border: 2px solid rgba(255,255,255,.2);
    box-shadow: 0 2px 4px var(--glow-accent)
}

.bws-module-container-main .bws-volume-slider::-webkit-slider-thumb:hover {
    background: var(--color-accent);
    box-shadow: 0 4px 8px var(--glow-accent-heavy)
}

.bws-module-container-main .bws-volume-slider::-moz-range-thumb,
.bws-volume-slider::-moz-range-thumb {
    width: 16px;
    height: 16px;
    border-radius: 50%;
    background: var(--color-accent);
    cursor: pointer;
    transition: all .2s;
    border: 2px solid rgba(255,255,255,.2);
    box-shadow: 0 2px 4px var(--glow-accent)
}

.bws-module-container-main .bws-volume-slider::-moz-range-thumb:hover {
    background: var(--color-accent);
    box-shadow: 0 4px 8px var(--glow-accent-heavy)
}

.bbp-spinner-wrapper {
    display: flex;
    align-items: stretch;
    gap: 0;
    width: auto;
    height: 32px
}

.bbp-spinner-wrapper input[type=number] {
    flex: 1
}

.bws-volume-slider {
    width: 100%;
    height: 6px;
    -webkit-appearance: none!important;
    appearance: none!important;
    background: linear-gradient(to right,var(--color-accent) 0%,var(--color-accent) var(--val, 75%),var(--slider-track-bg, rgba(255,255,255,0.15)) var(--val, 75%))!important;
    border-radius: 3px!important;
    border: 0!important;
    outline: 0!important;
    box-shadow: none!important;
    cursor: pointer
}

.bws-volume-slider:focus {
    outline: 0!important;
    box-shadow: none!important;
    border: 0!important
}

.bws-volume-slider::-webkit-slider-thumb {
    -webkit-appearance: none!important;
    appearance: none!important;
    width: 18px!important;
    height: 18px!important;
    border-radius: 50%!important;
    background: var(--color-accent)!important;
    border: 0!important;
    cursor: pointer!important;
    box-shadow: 0 2px 4px rgba(0,0,0,.3)!important;
    outline: 0!important
}

.bws-volume-slider::-webkit-slider-thumb:hover {
    box-shadow: 0 4px 8px rgba(0,0,0,.5)!important
}

.bws-volume-slider::-webkit-slider-runnable-track {
    background: 0 0!important;
    border: 0!important;
    outline: 0!important;
    box-shadow: none!important
}

.bws-volume-slider::-moz-range-thumb {
    width: 18px!important;
    height: 18px!important;
    border-radius: 50%!important;
    background: var(--color-accent)!important;
    border: 0!important;
    cursor: pointer!important;
    box-shadow: 0 2px 4px rgba(0,0,0,.3)!important;
    outline: 0!important
}

.bws-volume-slider::-moz-range-thumb:hover {
    box-shadow: 0 4px 8px rgba(0,0,0,.5)!important
}

.bws-volume-slider::-moz-range-track {
    background: 0 0!important;
    border: 0!important;
    outline: 0!important;
    box-shadow: none!important
}

.bws-volume-slider-container {
    display: flex;
    align-items: center;
    gap: .5rem;
    width: 100%;
    margin-left: .5rem;
    position: relative
}

/* Tick marks removed — slider track shows a clean fill only */

.sbp-pan-slider,.sbp-volume-slider {
    height: 4px;
    -webkit-appearance: none;
    background: rgba(255,255,255,.1);
    border-radius: 2px;
    outline: 0;
    border: 0;
    cursor: pointer;
    box-shadow: none;
    width: 100%;
    accent-color: var(--color-primary)
}

.sbp-pan-slider:focus,.sbp-volume-slider:focus {
    outline: 0;
    box-shadow: none;
    border: 0
}

.sbp-pan-slider::-webkit-slider-thumb,
.sbp-volume-slider::-webkit-slider-thumb {
    -webkit-appearance: none;
    width: 16px;
    height: 16px;
    border-radius: 50%;
    background: var(--color-accent);
    cursor: pointer;
    transition: all .2s;
    border: 2px solid rgba(255,255,255,.2);
    box-shadow: 0 2px 4px var(--glow-accent)
}

.sbp-pan-slider::-webkit-slider-thumb:hover {
    background: var(--color-accent);
    box-shadow: 0 4px 8px var(--glow-accent-heavy)
}

.sbp-pan-slider::-moz-range-thumb {
    width: 16px;
    height: 16px;
    border-radius: 50%;
    background: var(--color-accent);
    cursor: pointer;
    transition: all .2s;
    border: 2px solid rgba(255,255,255,.2);
    box-shadow: 0 2px 4px var(--glow-accent)
}

.sbp-pan-slider::-moz-range-thumb:hover {
    background: var(--color-accent);
    box-shadow: 0 4px 8px var(--glow-accent-heavy)
}

.sbp-volume-slider::-webkit-slider-thumb:hover {
    background: var(--color-accent);
    box-shadow: 0 4px 8px var(--glow-accent-heavy)
}

.sbp-volume-slider::-moz-range-thumb {
    width: 16px;
    height: 16px;
    border-radius: 50%;
    background: var(--color-accent);
    cursor: pointer;
    transition: all .2s;
    border: 2px solid rgba(255,255,255,.2);
    box-shadow: 0 2px 4px var(--glow-accent)
}

.sbp-volume-slider::-moz-range-thumb:hover {
    background: var(--color-accent);
    box-shadow: 0 4px 8px var(--glow-accent-heavy)
}

.sbp-range-slider-wrapper,
.sbp-effect-slider-container,
.bws-effect-slider-container {
    display: flex;
    align-items: center;
    gap: 12px;
    width: 100%
}

.sbp-range-slider-wrapper .sbp-range-slider {
    flex: 1;
    margin: 0
}

.sbp-range-values {
    display: flex;
    align-items: center;
    justify-content: flex-end;
    gap: 4px;
    min-width: 60px;
    font-size: 14px;
    font-weight: 600;
    color: var(--color-accent);
    white-space: nowrap
}

.sbp-range-slider,input.sbp-range-input {
    width: 100%;
    height: 6px;
    border: 0!important;
    outline: 0!important
}

.sbp-range-slider {
    position: relative;
    background: rgba(255,255,255,.1);
    border-radius: 2px;
    margin: 12px 0
}

/* Selected span + round thumbs, drawn on the bar — same look the native
   dual-range thumbs had, now as real elements we can position ourselves. */
.sbp-range-span {
    position: absolute;
    top: 0;
    height: 6px;
    background: var(--color-accent);
    border-radius: 2px;
    pointer-events: none
}

.sbp-range-handle {
    position: absolute;
    top: -6px;
    width: 18px;
    height: 18px;
    border-radius: 50%;
    background: var(--color-accent);
    box-shadow: 0 2px 4px rgba(0,0,0,.3);
    transform: translateX(-50%);
    cursor: pointer;
    touch-action: none;
    border: 0;
    outline: 0
}

.sbp-range-handle:hover,
.sbp-range-handle:active {
    box-shadow: 0 4px 8px rgba(0,0,0,.5)
}

.sbp-range-handle:focus-visible {
    box-shadow: 0 2px 4px rgba(0,0,0,.3), 0 0 0 3px var(--glow-accent, rgba(255,216,75,.35))
}

/* The inputs stay as the value model, addressed by id — they are just not the UI */
.sbp-range-slider input.sbp-range-input {
    display: none
}

input.sbp-range-input {
    position: absolute;
    top: 0;
    left: 0;
    -webkit-appearance: none!important;
    -moz-appearance: none!important;
    appearance: none!important;
    background: 0 0!important;
    padding: 0!important;
    margin: 0!important;
    pointer-events: none;
    box-shadow: none!important
}

.sbp-effect-slider:focus,input.sbp-range-input:focus {
    outline: 0!important;
    border: 0!important;
    box-shadow: none!important
}

input.sbp-range-input::-webkit-slider-thumb {
    pointer-events: all;
    -webkit-appearance: none!important;
    appearance: none!important;
    width: 18px;
    height: 18px;
    border-radius: 50%;
    background: var(--color-accent);
    cursor: pointer;
    border: 0!important;
    box-shadow: 0 2px 4px rgba(0,0,0,.3)!important;
    position: relative;
    top: 0;
    outline: 0!important
}

input.sbp-range-input::-webkit-slider-thumb:focus {
    outline: 0!important;
    box-shadow: 0 2px 4px rgba(0,0,0,.3)!important
}

.sbp-effect-slider::-webkit-slider-thumb:hover,input.sbp-range-input::-webkit-slider-thumb:hover {
    box-shadow: 0 4px 8px rgba(0,0,0,.5)!important
}

input.sbp-range-input::-webkit-slider-runnable-track {
    background: 0 0!important;
    border: 0!important;
    outline: 0!important;
    box-shadow: none!important
}

input.sbp-range-input::-moz-range-thumb {
    pointer-events: all;
    width: 18px;
    height: 18px;
    border-radius: 50%;
    background: var(--color-accent);
    cursor: pointer;
    border: 0!important;
    box-shadow: 0 2px 4px rgba(0,0,0,.3)!important;
    position: relative;
    top: 0;
    outline: 0!important
}

input.sbp-range-input::-moz-range-thumb:focus {
    outline: 0!important;
    box-shadow: 0 2px 4px rgba(0,0,0,.3)!important
}

input.sbp-range-input::-moz-range-thumb:hover {
    box-shadow: 0 4px 8px rgba(0,0,0,.5)!important
}

input.sbp-range-input::-moz-range-track {
    background: 0 0!important;
    border: 0!important;
    outline: 0!important;
    box-shadow: none!important
}

input.sbp-range-input::-moz-focus-outer {
    outline: 0!important;
    border: 0!important;
    box-shadow: none!important
}

input.sbp-range-min {
    z-index: 5
}

input.sbp-range-max {
    z-index: 6
}

.sbp-effects-container,
.bws-effects-container {
    display: flex;
    flex-direction: column;
    gap: 20px;
    padding: 12px 0
}

.sbp-effect-slider {
    flex: 1;
    height: 6px;
    -webkit-appearance: none!important;
    -moz-appearance: none!important;
    appearance: none!important;
    background: rgba(255,255,255,.1);
    border-radius: 2px;
    outline: 0!important;
    border: 0!important;
    padding: 0!important;
    margin: 0!important;
    box-shadow: none!important;
    position: relative
}

.sbp-effect-slider::-webkit-slider-thumb {
    -webkit-appearance: none!important;
    appearance: none!important;
    width: 18px;
    height: 18px;
    border-radius: 50%;
    background: var(--color-accent);
    cursor: pointer;
    border: 2px solid rgba(255,255,255,.3)!important;
    box-shadow: 0 2px 4px rgba(0,0,0,.3)!important;
    outline: 0!important
}

.sbp-effect-slider::-webkit-slider-thumb:focus {
    outline: 0!important;
    box-shadow: 0 2px 4px rgba(0,0,0,.3)!important
}

.sbp-effect-slider::-webkit-slider-runnable-track {
    background: 0 0!important;
    border: 0!important;
    outline: 0!important;
    box-shadow: none!important
}

.sbp-effect-slider::-moz-range-thumb {
    width: 18px;
    height: 18px;
    border-radius: 50%;
    background: var(--color-accent);
    cursor: pointer;
    border: 2px solid rgba(255,255,255,.3)!important;
    box-shadow: 0 2px 4px rgba(0,0,0,.3)!important;
    outline: 0!important
}

.sbp-effect-slider::-moz-range-thumb:hover {
    box-shadow: 0 4px 8px rgba(0,0,0,.5)!important
}

.sbp-effect-slider::-moz-range-thumb:focus {
    outline: 0!important;
    box-shadow: 0 2px 4px rgba(0,0,0,.3)!important
}

.sbp-effect-slider::-moz-range-track {
    background: 0 0!important;
    border: 0!important;
    outline: 0!important;
    box-shadow: none!important
}

.sbp-effect-slider::-moz-focus-outer {
    outline: 0!important;
    border: 0!important;
    box-shadow: none!important
}

.bbp-frequency-set.locked .bbp-frequency-input {
    pointer-events: none;
    opacity: .5
}

.bowl-frequency-control .bbp-frequency-input {
    width: 100%;
    height: 34px
}

.bbp-frequency-input {
    width: 120px;
    height: 34px;
    padding: 0 8px;
    border: 1px solid var(--color-accent);
    border-radius: 2px;
    text-align: right;
    background: var(--input-bg);
    color: var(--color-accent)!important;
    font-size: 11px;
    transition: all .2s;
    box-shadow: 0 0 8px var(--glow-accent-medium);
    box-sizing: border-box;
    -moz-appearance: textfield
}

.bbp-frequency-input:hover {
    background: var(--input-bg-hover);
    box-shadow: 0 0 12px rgba(255,216,75,.4)
}

.bbp-frequency-input:focus {
    outline: 0;
    border-color: var(--color-accent)!important;
    box-shadow: 0 0 16px var(--glow-accent-heavy);
    background: var(--input-bg-focus)
}

input[type=number].bbp-frequency-input::-moz-number-spin-box {
    background: 0 0;
    border: 0
}

input[type=number].bbp-frequency-input::-moz-number-spin-down,
input[type=number].bbp-frequency-input::-moz-number-spin-up {
    appearance: none;
    -moz-appearance: none;
    width: 24px;
    height: 16px;
    background-color: var(--bg-accent-light);
    border: 1px solid var(--border-accent-medium);
    color: var(--color-accent)!important;
    cursor: pointer;
    transition: all .2s;
    font-weight: 400;
    font-size: 10px
}

input[type=number].bbp-frequency-input::-moz-number-spin-up {
    border-bottom: 1px solid var(--border-accent-light)
}

input[type=number].bbp-frequency-input::-moz-number-spin-up::before {
    content: "▲"
}

input[type=number].bbp-frequency-input::-moz-number-spin-down::before {
    content: "▼"
}

input[type=number].bbp-frequency-input::-moz-number-spin-down:hover,
input[type=number].bbp-frequency-input::-moz-number-spin-up:hover {
    background-color: var(--border-accent-light)
}

input[type=number].bbp-frequency-input::-moz-number-spin-down:active,
input[type=number].bbp-frequency-input::-moz-number-spin-up:active {
    background-color: var(--glow-accent)
}

.bbp-frequency-input[type=number] {
    -moz-appearance: textfield
}

.sbp-config-input:hover {
    border-color: var(--color-accent)!important;
    background-color: var(--input-bg-hover);
    box-shadow: 0 0 15px var(--glow-accent-heavy)
}

.sbp-config-input:focus {
    outline: 0;
    border-color: var(--color-accent)!important;
    box-shadow: 0 0 20px var(--glow-accent-strong);
    background-color: var(--input-bg-focus)
}

.bbp-preset-control {
    display: flex;
    flex-direction: column;
    gap: 1rem;
    width: 100%
}

.bbp-frequency-set.dimmed .bbp-frequency-group {
    opacity: .5
}

.bbp-frequency-set.dimmed .bbp-waveform-controls {
    opacity: .5;
    pointer-events: none
}

.bbp-add-row-bottom-container.show,
.fsb-add-row-bottom-container.show {
    display: flex
}

.bws-module-title-container-toggle-button,.bws-module-title-container {
    display: flex;
    align-items: center;
    border: 1px solid var(--border-accent-light)
}

.bws-module-title-container {
    margin-left: -100px;
    margin-bottom: 1rem;
    padding: 1rem;
    border-radius: 4px;
    background-color: rgba(0,0,0,.8);
    backdrop-filter: blur(14px) saturate(1.15);
    -webkit-backdrop-filter: blur(14px) saturate(1.15);
    color: var(--color-accent)!important;
    font-size: 16px;
    font-weight: 600;
    transition: margin-left .3s ease;
    justify-content: flex-start;
    gap: 1rem;
    width: 300px
}

.bws-module-title-container-toggle-button {
    width: 35px;
    height: 35px;
    padding: 0;
    justify-content: center;
    border-radius: 2px;
    flex: 0 0 auto;
    min-width: unset;
    margin-left: auto;
    background: rgba(0,0,0,.2);
    color: var(--color-accent);
    cursor: pointer;
    transition: all .2s ease
}

.bws-module-title-container-toggle-button:hover {
    background: rgba(255,216,75,.1);
    box-shadow: 0 0 6px rgba(255,216,75,.2)
}

.bws-module-title-container-toggle-button.active {
    background: rgba(0,0,0,.2);
    box-shadow: none
}

.bws-module-title-container-toggle-button .caret-down,.bws-module-title-container-toggle-button .caret-up {
    transition: opacity .2s ease
}

.bws-module-title-container-toggle-button.active .caret-up {
    display: none!important
}

.bws-module-title-container-toggle-button.active .caret-down {
    display: block!important
}

.bws-module-title-container-toggle-button:not(.active) .caret-up {
    display: block!important
}

.bws-module-title-container-toggle-button:not(.active) .caret-down {
    display: none!important
}

.bws-module-title-row {
    display: flex;
    align-items: stretch;
    gap: 1rem;
    margin-bottom: 1rem
}

.bws-module-title-row .bws-module-title-container {
    margin-bottom: 0
}

.bws-title-session-actions {
    display: flex;
    align-items: center;
    gap: .6rem;
    padding: 0 .8rem;
    border: 1px solid var(--border-accent-light);
    border-radius: 4px;
    background-color: #000
}

/* The sync info card is a short text card, not a full preset sheet. */
.bt-info-modal.bt-sync-info-modal,
.ft-info-modal.ft-sync-info-modal,
.it-info-modal.it-sync-info-modal { max-width: 560px }

.bws-title-session-actions .hpp-melody-action-btn {
    width: 38px;
    height: 38px
}

@media screen and (max-width:1379px) {
    .bws-module-title-container {
        margin-left: 0
    }
}

.bbp-player-logo-video {
    max-width: 80px;
    max-height: 80px;
    border-radius: 4px;
    object-fit: contain;
    flex-shrink: 0
}

.bbp-frequency-group.playing {
    border-color: var(--color-accent)!important;
    box-shadow: 0 0 12px var(--glow-accent-strong),inset 0 0 8px var(--border-accent-light)
}

.bws-module-container-main {
    position: relative;
    font-family: 'Roboto',-apple-system,BlinkMacSystemFont,"Segoe UI","Helvetica Neue",sans-serif;
    max-width: 1200px;
    width: 100%;
    margin: 2rem auto;
    padding-inline: .5rem;
    border-radius: 6px;
    color: rgba(255,255,255,.9);
    gap: clamp(5px,1vw,1rem);
    padding-top: 15px;
    /* Real bottom padding, not a border: without either, the last child's
       `margin-bottom: 1rem` (.bws-module-content) is adjoining to this box's
       bottom edge and collapses out of it, so the panel would end flush against
       its last child. */
    padding-bottom: 2px;
    padding-left: max(.5rem,15px);
    padding-right: max(.5rem,15px);
    background: linear-gradient(135deg,var(--bg-dark-heavy) 0%,var(--bg-dark-medium) 50%,rgba(255,255,255,.3) 100%);
    /* Frosted glass: the panel's background is already semi-transparent, so blurring
       what shows through it reads as thick glass over the page background. */
    backdrop-filter: blur(16px) saturate(1.15);
    -webkit-backdrop-filter: blur(16px) saturate(1.15);
    box-sizing: border-box
}

.bbp-controls-section,
.bws-controls-left,
.bws-controls-right {
    display: flex;
    flex-direction: column;
    gap: 1rem
}

.bws-volume-control label {
    font-size: 14px;
    background: 0 0;
    border: 0
}

.bbp-preset-to-session-container,
.bws-volume-control {
    display: flex;
    align-items: center;
    gap: 1rem
}

.bws-volume-control {
    flex: 1;
    flex-direction: row;
    width: 100%;
    gap: .5rem
}

.bws-volume-control svg {
    flex-shrink: 0;
    width: 24px;
    height: 24px
}

.bbp-row-volume-control.bws-volume-control svg,
.bbp-frequency-set .bws-volume-control svg,
.fsb-row-volume-control.bws-volume-control svg {
    width: 19px;
    height: 19px
}

.bws-volume-control label {
    margin-right: 1rem;
    color: var(--text-muted);
    font-weight: 500
}

.bbp-session-row {
    display: flex;
    flex-direction: column;
    gap: 1rem;
    margin-bottom: 42px;
    position: relative;
    opacity: 1;
    transform: translateY(0);
    transition: all .3s ease-out
}

.fsb-row-volume-control {
    display: flex;
    align-items: center;
    gap: .5rem;
    width: 100%;
    flex-basis: 100%;
    padding: .5rem .75rem;
    background: var(--bg-dark-lighter);
    border-radius: 4px;
    margin-top: .5rem
}

.bbp-row-frequency-control {
    flex: 1;
    display: flex;
    flex-direction: column;
    gap: .5rem
}

.bbp-session-controls,
.sbp-session-controls {
    display: flex;
    gap: 1rem;
    align-items: center;
    padding: 1rem;
    background: var(--bg-dark-heavy);
    border-radius: 4px;
    border: 1px solid var(--border-accent-medium)
}

.sbp-session-controls {
    flex-direction: column;
    align-items: flex-start
}

.sbp-controls-row {
    display: flex;
    justify-content: space-between;
    gap: 1rem;
    flex-wrap: wrap
}

.sbp-controls-left,
.sbp-controls-middle {
    display: flex;
    align-items: flex-end;
    gap: 1rem
}

.sbp-bowls-container {
    display: flex;
    flex-direction: column;
    gap: clamp(5px,1vw,1rem);
    padding: clamp(5px,1vw,1rem)
}

.sbp-config-section {
    gap: .75rem;
    padding: 1rem;
    background: rgba(0,0,0,.3);
    border-radius: 2px;
    border: 1px solid var(--border-accent-light)
}

.bowl-frequency-control {
    display: flex;
    flex-direction: column;
    gap: .5rem;
    align-items: center
}

.bowl-frequency-control>label {
    background-color: #000!important;
    border-color: #000!important
}

.bowl-image,.bowl-image-container {
    display: flex;
    align-items: center;
    justify-content: center
}

.bowl-image-container {
    width: calc(100% + 1rem);
    height: 150px;
    margin: -.2rem -.5rem -.5rem;
    background-color: #000;
    border-radius: 4px
}

.bowl-image {
    width: 80%;
    aspect-ratio: 1;
    position: relative;
    margin: .3rem 0;
    isolation: isolate;
    background: 0 0;
    border: 0
}

.bowl-row {
    display: flex;
    gap: clamp(2px,.4vw,1rem)
}

.bowls-frequency-group {
    display: flex;
    flex-direction: column;
    gap: .5rem;
    position: relative;
    padding: .8rem .5rem .5rem;
    border: 1px solid var(--border-accent-light);
    border-radius: 4px;
    background-color: var(--bg-dark-light);
    transition: all .3s ease;
    flex: 1;
    min-width: 0
}

.bowls-frequency-group.playing {
    border-color: var(--color-accent)!important;
    box-shadow: 0 0 12px var(--glow-accent-strong),inset 0 0 8px var(--border-accent-light)
}

.bowls-frequency-group.hit-glow {
    animation: hitGlowFade .7s ease-in-out
}

.bowls-frequency-group.hit-glow-fade-in {
    animation: hitGlowFadeIn .2s ease-in
}

.bowls-frequency-group.slide-shimmer {
    animation: slideShimmer .5s ease-in-out infinite
}

.bbp-module-toggle.active .bbp-toggle-on {
    opacity: 1
}

.bbp-session-row:last-child .bbp-add-row-between svg {
    width: 16px;
    height: 16px
}

#frequency-set-oster.visible {
    width: auto;
    height: auto;
    overflow: visible;
    flex: 1;
    gap: 1rem;
    padding: 0;
    margin: 0
}

.bbp-row-main.playing,
.fsb-row-main.playing {
    border-color: var(--color-accent)!important;
    box-shadow: 0 0 12px var(--glow-accent-strong),inset 0 0 8px var(--border-accent-light)
}

.bbp-horizontal-row.playing,
.fsb-horizontal-row.playing {
    border-color: var(--color-accent)!important;
    box-shadow: 0 0 12px var(--glow-accent-strong),inset 0 0 8px var(--border-accent-light)
}

.bbp-horizontal-row .duration,
.fsb-horizontal-row .duration {
    display: none
}

.bbp-session-row.adding,
.fsb-session-row.adding {
    opacity: 0;
    transform: translateY(20px)
}

.bbp-session-row.removing,
.fsb-session-row.removing {
    opacity: 0;
    transform: translateY(-10px);
    margin-bottom: 0;
    max-height: 0;
    overflow: hidden;
    transition: all .3s ease
}

.sbp-effect-row {
    display: flex;
    flex-direction: column;
    gap: 6px
}

.bbp-toggle-text {
    position: absolute;
    font-size: 8px;
    font-weight: 400;
    text-align: center;
    line-height: 26px;
    transition: all .3s ease;
    text-transform: uppercase;
    letter-spacing: .5px
}

.bws-volume-display {
    font-size: 13px;
    color: #fff!important;
    font-weight: 400;
    min-width: 30px
}

.sbp-pan-label,.sbp-volume-label {
    display: flex;
    justify-content: space-between;
    align-items: center;
    width: 100%
}

.bws-label {
    font-family: 'Roboto',-apple-system,BlinkMacSystemFont,"Segoe UI","Helvetica Neue",sans-serif;
    font-size: 13px;
    font-weight: 400;
    color: var(--color-accent);
    text-transform: uppercase;
    letter-spacing: .5px
}

.sbp-effect-label,
.bws-effect-label {
    font-size: 13px;
    font-weight: 600;
    color: var(--color-accent);
    text-transform: capitalize
}

.bws-group-description {
    font-size: 13px;
    color: rgba(255,255,255,.9);
    background-color: transparent;
    border: 0
}

.bowl-frequency-label {
    display: flex;
    justify-content: center;
    align-items: center;
    width: 100%
}

/* Field labels inside the bowl controls read like the preset filter labels
   (.bws-fbar-field label): small, bold, uppercase, muted. */
.bowl-frequency-title {
    font-size: 10px;
    font-weight: 700;
    letter-spacing: .06em;
    text-transform: uppercase;
    color: var(--text-muted, #aaa)
}

.bbp-module-toggle.active {
    background-color: #28a745
}

.bbp-module-toggle.inactive {
    background-color: #888
}

.bbp-module-toggle.active .bbp-toggle-off {
    opacity: 0
}

body {
    font-family: 'Roboto',-apple-system,BlinkMacSystemFont,"Segoe UI","Helvetica Neue",sans-serif;
    background-color: #000;
    /* The gradient layer paints over the photo (first layer = topmost), dimming it
       50% without touching the file. */
    background-image: linear-gradient(rgba(0,0,0,.5), rgba(0,0,0,.5)), url("../ASSETS/background.jpg");
    background-size: cover;
    background-position: center;
    background-attachment: fixed;
    background-repeat: no-repeat;
    margin: 0;
    min-height: 100vh
}

.bowl-image::after {
    content: "";
    z-index: -1;
    pointer-events: none
}

.bowl-image img {
    width: 100%;
    height: auto;
    object-fit: contain;
    transition: transform .6s ease-in-out;
    border-radius: 8px;
    mix-blend-mode: normal;
    z-index: 1;
    will-change: transform
}

.bowl-image::after {
    position: absolute;
    bottom: -10px;
    left: 50%;
    transform: translateX(-50%);
    width: 70%;
    height: 16px;
    background: radial-gradient(ellipse at center,rgba(0,0,0,.2)0,transparent 70%);
    transition: opacity .6s ease-in-out;
    opacity: .15
}

.bowl-image img,.bowl-image.stopping img {
    transform: translateY(0)
}

.bowl-image.stopping::after {
    opacity: .15;
    transform: translateX(-50%) scale(1)
}

.bbp-module-toggle,body {
    padding: 0;
    position: relative
}

.bbp-module-toggle {
    width: 44px;
    height: 21px;
    background-color: #888;
    border: 0;
    border-radius: 11px;
    cursor: pointer;
    transition: all .3s ease;
    display: flex;
    align-items: center;
    overflow: hidden
}

.bbp-toggle-off,.bbp-toggle-on {
    width: 16px;
    transition: opacity .3s ease
}

.bbp-toggle-on {
    left: 4px;
    color: #fff;
    opacity: 0
}

.bbp-toggle-off {
    right: 4px;
    color: #000;
    opacity: 1
}

@supports (-moz-appearance:none) {
    .bbp-frequency-input {
        width: 60px
    }
}

@media (max-width:1024px) {
    .bws-icon-button {
        flex: 0 0 calc(50% - .5rem)
    }
}

@media (max-width:768px) {

    .bws-icon-button {
        flex: 1
    }

    .bbp-session-row {
        flex-direction: column
    }
}

@media (max-width:768px) {
    .bowl-frequency-control>label,.sbp-pan-label,.sbp-pan-slider,.sbp-volume-label,.sbp-volume-slider {
        display: none
    }

    .bowl-frequency-control {
        flex-direction: column;
        align-items: flex-start;
        gap: .5rem;
        width: 100%
    }

    .bowl-frequency-control .bbp-spinner-wrapper {
        width: 100%;
        box-sizing: border-box
    }

    .bowl-frequency-control .bbp-frequency-input {
        text-align: center
    }

    .bowl-frequency-control .bbp-spinner-buttons,.bowl-frequency-label {
        display: none
    }

    .bowls-frequency-group {
        padding: .2rem
    }

    .bowl-image-container {
        display: none
    }

    .sbp-controls-row {
        flex-direction: column
    }

    .bbp-spinner-wrapper,
    .sbp-auto-dist-btn,
    .sbp-auto-pan-btn,
    .sbp-controls-left,
    .sbp-controls-middle {
        width: 100%
    }

    .sbp-bowl-btn,
    .sbp-controls-left,
    .sbp-controls-middle {
        justify-content: center
    }

    .sbp-bowl-btn {
        position: relative;
        padding: 8px;
        height: 35px;
        display: flex;
        align-items: center;
        font-size: 0
    }

    .sbp-depth-btn::after,.sbp-hit-btn::after,.sbp-play-btn::after,.sbp-slide-btn::after {
        width: 16px;
        height: 16px;
        display: block
    }

    .sbp-play-btn::after {
        content: url("data:image/svg+xml,%3Csvg viewBox='0 0 24 24' fill='white' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M8 5v14l11-7z'/%3E%3C/svg%3E")
    }

    .sbp-bowl-btn .desktop-text {
        display: none!important
    }

    .sbp-depth-btn::after {
        content: url("data:image/svg+xml,%3Csvg viewBox='0 0 24 24' fill='white' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm0 18c-4.42 0-8-3.58-8-8s3.58-8 8-8 8 3.58 8 8-3.58 8-8 8z'/%3E%3Cpath d='M12 6c-3.31 0-6 2.69-6 6s2.69 6 6 6 6-2.69 6-6-2.69-6-6-6z'/%3E%3C/svg%3E")
    }

    .sbp-hit-btn::after {
        content: url("data:image/svg+xml,%3Csvg viewBox='0 0 24 24' fill='white' xmlns='http://www.w3.org/2000/svg'%3E%3Ccircle cx='12' cy='12' r='10'/%3E%3Ccircle cx='12' cy='12' r='4'/%3E%3C/svg%3E")
    }

    .sbp-slide-btn::after {
        content: url("data:image/svg+xml,%3Csvg viewBox='0 0 24 24' fill='white' xmlns='http://www.w3.org/2000/svg'%3E%3Crect x='2' y='8' width='20' height='8' rx='2'/%3E%3C/svg%3E")
    }

    .sbp-auto-dist-btn,.sbp-auto-pan-btn {
        font-size: 0
    }

    .sbp-auto-pan-btn::after {
        content: "AP";
        font-size: 11px
    }

    .sbp-auto-dist-btn::after {
        content: "AD";
        font-size: 11px
    }
}

@media (max-width:460px) {
    .bowl-frequency-control .bbp-frequency-input {
        font-size: clamp(9px,3vw,12px)
    }

    .bbp-frequency-input {
        padding: 0 3px
    }
}

.bbp-module-toggle-animated {
    transition: opacity .4s ease,transform .4s ease,max-height .4s ease,margin .4s ease;
    overflow: hidden
}

.bbp-module-toggle-animated.bbp-module-visible {
    animation: moduleShowAnimation .4s ease forwards;
    opacity: 1;
    transform: translateY(0);
    max-height: 10000px
}

/* The 10000px above is a stand-in for "no limit" — max-height cannot animate to
   auto, so the open animation needs a finite target. It was never meant to be a
   real ceiling, but animation-fill-mode: forwards makes it stick, and any module
   whose content grows past it (a composition builder with many sections and rows)
   was clipped at that line with overflow hidden and no scrollbar.
   Once the animation has finished there is nothing left to interpolate, so the
   cap comes off. `animation: none` is required, not decorative: a filled
   animation outranks normal declarations in the cascade, so max-height would
   otherwise stay pinned at 10000px. Applied by js/toggle-manager.js on
   animationend, and removed before collapsing so the hide animation still runs. */
.bbp-module-toggle-animated.bbp-module-visible.bbp-module-open {
    animation: none;
    max-height: none;
}

.bbp-module-toggle-animated.bbp-module-hidden {
    animation: moduleHideAnimation .4s ease forwards;
    opacity: 0;
    transform: translateY(-20px);
    max-height: 0;
    overflow: hidden;
    pointer-events: none
}

.bowl-hit-button-line {
    position: absolute;
    bottom: 4px;
    left: 10px;
    right: 10px;
    width: calc(100% - 20px);
    height: 4px;
    background-color: var(--bg-dark-lighter);
    overflow: hidden
}

.bowl-hit-button-line-fill {
    width: 0%;
    height: 100%;
    background-color: var(--color-accent)!important;
    transition: width .05s linear
}

.bowl-image.playing img {
    transform: translateY(-15px)
}

.bowl-image.playing::after {
    opacity: .25;
    transform: translateX(-50%) scale(1.1)
}

.bowl-image.depth-active img {
    filter: brightness(1.15)
}

.bowl-image.hit:not(.playing) img {
    animation: hitPulseGround .5s ease-out!important
}

.bowl-image.hit.playing img {
    animation: hitPulseHover .5s ease-out!important
}

.bowl-image.sliding:not(.playing) img {
    animation: slideWobbleGround 2s infinite ease-in-out!important
}

.bowl-image.sliding.playing img {
    animation: slideWobbleHover 2s infinite ease-in-out!important
}

.bws-module-content {
    display: flex;
    flex-direction: column;
    width: 100%;
    padding: 1rem;
    background: var(--bg-dark-heavy);
    border-radius: 4px;
    border: 1px solid var(--border-accent-medium);
    margin: 1rem 0;
    box-sizing: border-box;
    gap: 1rem
}

@media screen and (min-width:769px) and (max-width:1024px) {
    .bws-module-container-main {
        margin: 1.5rem auto;
        padding: 12px 12px 24px
    }

    .bws-module-content {
        padding: .875rem;
        margin: .75rem 0;
        gap: .875rem
    }

    .bws-group-description {
        font-size: 11px
    }

    .bbp-controls-section {
        gap: .875rem
    }

    .bws-icon-button {
        min-height: 55px;
        padding: .375rem;
        font-size: 10px
    }

    .bbp-session-controls,
    .sbp-session-controls {
        padding: .875rem;
        gap: .875rem
    }

    .bbp-preset-to-session-container {
        gap: .875rem
    }

    #oster-beat-slider {
        display: none
    }
}

@media screen and (max-width:768px) {
    .bws-module-container-main {
        margin: 1rem auto;
        padding: 10px 10px 20px;
        gap: clamp(3px,1vw,.75rem)
    }

    .bws-module-title-container {
        font-size: 1.25rem;
        margin-bottom: .5rem
    }

    .bws-module-title-row {
        margin-bottom: .5rem
    }

    .bws-module-title-row .bws-module-title-container {
        margin-bottom: 0
    }

    .bws-module-content {
        padding: .75rem;
        margin: .5rem 0;
        gap: .75rem
    }

    .bws-group-description {
        font-size: 11px
    }

    .bbp-controls-section {
        gap: .75rem
    }

    .bbp-module-toggle {
        align-self: flex-start;
        order: 2
    }

    .bbp-preset-control {
        gap: .5rem
    }

    .bbp-slider-input-group,
    .bws-volume-control {
        flex-direction: column;
        gap: .5rem
    }

    .bbp-frequency-input {
        padding: 0 8px
    }

    .bbp-frequency-input,.bws-dropdown {
        width: 100%;
        font-size: 12px
    }

    .bws-icon-button {
        flex: 1;
        min-height: 50px;
        padding: .3rem;
        font-size: 9px;
        box-sizing: border-box;
        min-width: 0
    }

    .bws-icon-button svg {
        height: 20px
    }

    .bws-volume-slider-container {
        flex-direction: column;
        gap: .25rem
    }

    .bws-volume-slider {
        width: 100%
    }

    .bbp-session-controls,
    .sbp-session-controls {
        flex-direction: column;
        padding: .75rem;
        gap: .75rem
    }

    .bbp-preset-to-session-container {
        width: 100%
    }

    #oster-beat-slider {
        display: none
    }
}

@media screen and (max-width:480px) {
    .bws-module-container-main {
        margin: .75rem auto;
        padding: 8px 8px 16px
    }

    .bws-module-title-container {
        font-size: 1.1rem
    }

    .bws-module-content {
        padding: .5rem;
        margin: .25rem 0;
        gap: .5rem
    }

    .bws-icon-button {
        flex: 1;
        min-height: 45px;
        padding: .25rem;
        font-size: 8px;
        box-sizing: border-box;
        min-width: 0
    }

    .bws-button-a {
        font-size: 11px;
        padding: .5rem
    }

    .bbp-frequency-input {
        font-size: 11px;
        padding: 0 8px;
        height: 32px
    }

    .bws-dropdown {
        font-size: 11px
    }

    .bws-volume-control svg {
        width: 20px;
        height: 20px
    }

    #oster-beat-slider {
        display: none
    }
}

.sbp-effect-value {
    min-width: 45px;
    text-align: right;
    font-size: 13px;
    font-weight: 600;
    color: var(--color-accent)
}

.bws-preset-row {
    padding: 0 1rem .75rem
}

/* The drum and gong players' type dropdowns moved out of the module header and
   into the left control column, where the 1rem side padding of the header strip
   would inset them against the groups below. Stacked label over dropdown, with
   the handpan preset column's own gap. */
.sdp-drum-type-row,
.sgp-gong-type-row {
    display: flex;
    flex-direction: column;
    gap: .35rem;
    padding: 0 0 .75rem
}

.sgp-future-group {
    width: 720px;
    min-height: 720px;
    background: #000;
    border: 1px solid var(--border-accent-light);
    border-radius: 4px;
    overflow: hidden
}

.sgp-gong-wrap,
.sdp-drum-wrap {
    position: relative;
    width: 720px;
    height: 760px;
    cursor: crosshair;
    user-select: none;
    -webkit-user-select: none;
    overflow: hidden
}

.sgp-gong-content,
.sdp-drum-content {
    position: absolute;
    top: 40px;
    left: 0;
    right: 0;
    height: 720px;
    overflow: hidden
}

.sgp-gong-svg,
.sgp-gong-image,
.sdp-drum-svg,
.sdp-drum-image {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    pointer-events: none
}

/* Instrument artwork — one 720x720 image per gong / drum type, swapped by the
   Type dropdown, in place of the old video clips. Both players stack two copies of
   the same picture: this base layer is the whole scene (instrument + wooden stand)
   and is always static. */
.sgp-gong-image,
.sdp-drum-image {
    object-fit: cover;
    z-index: 0;
    -webkit-user-drag: none
}

/* Strike layer — the same picture masked down to the instrument itself, sitting
   exactly on top of the base copy. The strike pulse scales and brightens only this,
   so a hit moves the gong disc / drum head while the stand behind it stays perfectly
   still. Both stands are the same prop, so the masks differ only in the measured
   geometry of what hangs in them. The 4% soft edge lands in the black gap between
   the instrument and the stand, which is why the seam never shows. */
.sgp-gong-image--disc,
.sdp-drum-image--head {
    z-index: 0;
    will-change: transform, filter;
    -webkit-mask-image: radial-gradient(ellipse closest-side, #000 0 96%, rgba(0,0,0,0) 100%);
            mask-image: radial-gradient(ellipse closest-side, #000 0 96%, rgba(0,0,0,0) 100%);
    -webkit-mask-repeat: no-repeat;
            mask-repeat: no-repeat
}

/* Gong disc — centre ~(358,360) of 720, radii ~231 x 229. Ending shape 242 x 240,
   i.e. mask-size 484 x 480 px positioned at (116,120), as percentages so it
   survives a resize. */
.sgp-gong-image--disc {
    transform-origin: 49.7% 50%;
    -webkit-mask-size: 67.22% 66.67%;
            mask-size: 67.22% 66.67%;
    -webkit-mask-position: 49.15% 50%;
            mask-position: 49.15% 50%
}

/* Drum head — centre ~(356,356) of 720, radii ~234 x 230. Ending shape 246 x 238,
   i.e. mask-size 492 x 476 px positioned at (110,118). */
.sdp-drum-image--head {
    transform-origin: 49.4% 49.4%;
    -webkit-mask-size: 68.33% 66.11%;
            mask-size: 68.33% 66.11%;
    -webkit-mask-position: 48.2% 48.4%;
            mask-position: 48.2% 48.4%
}

.sgp-gong-svg,
.sdp-drum-svg {
    display: block;
    z-index: 1;
    transition: filter .08s linear
}

.sgp-sound-circle, .sdp-sound-circle {
    stroke-width: 1.5;
    transition: fill .15s,stroke .15s
}

.sgp-sound-circle--blue, .sdp-sound-circle--blue {
    fill: rgba(70,160,255,.08);
    stroke: rgba(70,160,255,.55)
}

.sgp-sound-circle--purple, .sdp-sound-circle--purple {
    fill: rgba(170,90,240,.08);
    stroke: rgba(170,90,240,.55)
}

.sgp-sound-circle--green, .sdp-sound-circle--green {
    fill: rgba(60,220,110,.08);
    stroke: rgba(60,220,110,.55)
}

.sgp-sound-circle--white, .sdp-sound-circle--white {
    fill: rgba(255,255,255,.08);
    stroke: rgba(255,255,255,.55)
}

.sgp-sound-circle-group, .sdp-sound-circle-group {
    cursor: pointer;
    pointer-events: all
}

.sgp-sound-circle-group:hover .sgp-sound-circle--blue,
.sdp-sound-circle-group:hover .sdp-sound-circle--blue {
    fill: rgba(70,160,255,.22);
    stroke: rgba(70,160,255,.9)
}

.sgp-sound-circle-group:hover .sgp-sound-circle--purple,
.sdp-sound-circle-group:hover .sdp-sound-circle--purple {
    fill: rgba(170,90,240,.22);
    stroke: rgba(170,90,240,.9)
}

.sgp-sound-circle-group:hover .sgp-sound-circle--green,
.sdp-sound-circle-group:hover .sdp-sound-circle--green {
    fill: rgba(60,220,110,.22);
    stroke: rgba(60,220,110,.9)
}

.sgp-sound-circle-group:hover .sgp-sound-circle--white,
.sdp-sound-circle-group:hover .sdp-sound-circle--white {
    fill: rgba(255,255,255,.22);
    stroke: rgba(255,255,255,.9)
}

.sgp-sound-circle-group.active .sgp-sound-circle--blue,
.sdp-sound-circle-group.active .sdp-sound-circle--blue {
    fill: rgba(70,160,255,.32);
    stroke: #46a0ff
}

.sgp-sound-circle-group.active .sgp-sound-circle--purple,
.sdp-sound-circle-group.active .sdp-sound-circle--purple {
    fill: rgba(170,90,240,.32);
    stroke: #aa5af0
}

.sgp-sound-circle-group.active .sgp-sound-circle--green,
.sdp-sound-circle-group.active .sdp-sound-circle--green {
    fill: rgba(60,220,110,.32);
    stroke: #3cdc6e
}

.sgp-sound-circle-group.active .sgp-sound-circle--white,
.sdp-sound-circle-group.active .sdp-sound-circle--white {
    fill: rgba(255,255,255,.32);
    stroke: #fff
}

.sgp-ripple-layer,
.sdp-drum-ripple-layer {
    position: absolute;
    inset: 0;
    pointer-events: none;
    z-index: 2
}

.sgp-ripple {
    position: absolute;
    border-radius: 50%;
    pointer-events: none;
    width: 32px;
    height: 32px;
    border: 2px solid rgba(255,216,75,.75);
    transform: translate(-50%,-50%) scale(0);
    animation: sgp-ripple-out 1.6s ease-out forwards
}

.sgp-charge-indicator,.sgp-gong-hint {
    position: absolute;
    transform: translateX(-50%)
}

.sgp-gong-hint {
    bottom: 1rem;
    left: 50%;
    z-index: 4;
    font-size: 11px;
    color: var(--text-muted);
    text-align: center;
    letter-spacing: .03em;
    background: rgba(0,0,0,.55);
    padding: 2px 10px;
    border-radius: 10px;
    white-space: nowrap
}

.sgp-charge-indicator {
    width: 35px;
    height: 10px;
    border: 1px solid #ffd84b;
    background: 0 0;
    pointer-events: none;
    z-index: 10;
    box-sizing: border-box
}

.sgp-charge-fill,
.sdp-charge-fill,
.hpp-charge-fill {
    height: 100%;
    background: rgba(255,216,75,.9);
    width: 0%;
    pointer-events: none
}

.bws-bottom-row {
    display: flex;
    gap: 1rem;
    margin: 0 1rem 1rem
}

.sgp-humanize-group,.sgp-rhythm-group {
    flex: 1;
    padding: 1rem;
    background: var(--bg-dark-heavy);
    border: 1px solid var(--border-accent-light);
    border-radius: 4px
}

.bws-group-header {
    color: var(--color-accent);
    font-size: 12px;
    font-weight: 700;
    letter-spacing: .08em;
    text-transform: uppercase;
    margin-bottom: .875rem
}

/* ── Per-group settings info button + modal (drum / gong, mirrors handpan) ── */
.bws-group-header-row { display: flex; align-items: center; justify-content: space-between; gap: .5rem; margin-bottom: .875rem }
.bws-group-header-row .bws-group-header { margin-bottom: 0 }
/* .bws-group-info is a behaviour hook only — the look comes from .bws-info-badge,
   which every one of these buttons also carries. */
.bws-sim-overlay {
    position: fixed; inset: 0; z-index: 9000;
    display: flex; align-items: center; justify-content: center; gap: 16px; padding: 24px; overflow: hidden;
    font-family: 'Roboto', -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif
}
.bws-sim-modal {
    width: 100%; max-width: 560px; max-height: calc(100vh - 100px); display: flex; flex-direction: column;
    border: 1px solid var(--color-accent); border-radius: 8px;
    box-shadow: 0 0 12px rgba(255,216,75,.4), 0 8px 32px rgba(0,0,0,.7); overflow: hidden
}
.bws-sim-head { display: flex; align-items: center; gap: .6rem; padding: 1rem 1rem .75rem; flex-shrink: 0 }
/* .bws-sim-badge look comes from .bws-info-badge */
.bws-sim-title { flex: 1; color: var(--text-primary, #fff); font-size: 16px; font-weight: 700 }
.bws-sim-close {
    width: 28px; height: 28px; flex-shrink: 0; border-radius: 50%; border: 0; cursor: pointer;
    background: rgba(211,211,211,1); color: rgba(20,20,20,.95); font-size: 12px; line-height: 1;
    display: flex; align-items: center; justify-content: center
}
.bws-sim-close:hover { background: #f44336; color: #fff }
.bws-sim-underline { height: 1px; background: rgba(128,128,128,.4); margin: 0 1rem; flex-shrink: 0 }
.bws-sim-body { overflow-y: auto; padding: 1.25rem 1rem; scrollbar-width: thin; scrollbar-color: var(--color-accent) #000 }
.bws-sim-body::-webkit-scrollbar { width: 8px }
.bws-sim-body::-webkit-scrollbar-track { background: #000 }
.bws-sim-body::-webkit-scrollbar-thumb { background: var(--color-accent); border-radius: 4px }
.bws-sim-subtitle { color: rgba(248,249,250,.9); font-size: 13px; font-weight: 500; font-style: italic; margin-bottom: 12px }
.bws-sim-item { padding: .7rem 0; border-bottom: 1px solid rgba(255,216,75,.12) }
.bws-sim-item:last-child { border-bottom: 0 }
.bws-sim-name { color: var(--color-accent); font-size: 14px; font-weight: 700; margin-bottom: 4px }
.bws-sim-text { color: rgba(248,249,250,.85); font-size: 13px; line-height: 19px }
@media (max-width: 560px) {
    .bws-sim-overlay { padding: 10px; gap: 6px }
}

.bws-effect-row {
    display: flex;
    flex-direction: column;
    gap: 8px
}

.bws-effect-slider {
    flex: 1;
    height: 6px;
    -webkit-appearance: none!important;
    appearance: none!important;
    background: linear-gradient(to right,var(--color-accent) 0%,var(--color-accent) var(--val, 50%),var(--slider-track-bg, rgba(255,255,255,0.15)) var(--val, 50%))!important;
    border-radius: 3px!important;
    border: 0!important;
    outline: 0!important;
    box-shadow: none!important;
    cursor: pointer
}

.bws-effect-slider::-webkit-slider-thumb {
    -webkit-appearance: none!important;
    appearance: none!important;
    width: 18px;
    height: 18px;
    border-radius: 50%;
    background: var(--color-accent)!important;
    border: 0!important;
    cursor: pointer;
    box-shadow: 0 2px 4px rgba(0,0,0,.3)!important;
    outline: 0!important
}

.bws-effect-slider::-webkit-slider-thumb:hover {
    box-shadow: 0 4px 8px rgba(0,0,0,.5)!important
}

.bws-effect-slider::-webkit-slider-runnable-track {
    background: 0 0!important;
    border: 0!important;
    outline: 0!important;
    box-shadow: none!important
}

.bws-effect-slider::-moz-range-thumb {
    width: 18px;
    height: 18px;
    border-radius: 50%;
    background: var(--color-accent)!important;
    border: 0!important;
    cursor: pointer;
    box-shadow: 0 2px 4px rgba(0,0,0,.3)!important;
    outline: 0!important
}

.bws-effect-slider::-moz-range-track {
    background: 0 0!important;
    border: 0!important;
    outline: 0!important;
    box-shadow: none!important
}

.bws-effect-slider:focus {
    outline: 0!important;
    border: 0!important;
    box-shadow: none!important
}

.bws-effect-value {
    min-width: 52px;
    text-align: right;
    font-size: 13px;
    font-weight: 400;
    color: #fff;
    flex-shrink: 0
}

.bws-preset-description {
    display: flex;
    flex-direction: column;
    gap: .35rem;
    margin: 0 1rem 1rem;
    padding: .875rem 1rem;
    background: var(--bg-dark-heavy);
    border: 1px solid var(--border-accent-light);
    border-radius: 4px;
    font-size: 13px;
    line-height: 1.5
}

.bws-preset-name {
    color: var(--color-accent);
    font-size: 13px;
    font-weight: 700;
    letter-spacing: .04em
}

.bws-preset-text {
    color: var(--text-muted);
    font-size: 12px;
    line-height: 1.55
}

.bws-controls-grid {
    display: flex;
    gap: 1rem;
    padding: 0 1rem 1rem;
    align-items: flex-start
}

.bws-controls-left {
    flex: 1;
    min-width: 0
}

.bws-controls-right {
    flex-shrink: 0;
    width: 720px
}

.bws-control-group {
    background: var(--bg-dark-heavy);
    border: 1px solid var(--border-accent-light);
    border-radius: 4px;
    padding: 1rem
}

.bws-seq-label,
.bws-seq-beat-num,
.sdp-seq-row-title,
.sgp-seq-row-title,
.hpp-seq-row-title {
    font-size: 11px;
    font-weight: 700;
    color: var(--color-accent);
    text-transform: uppercase;
    letter-spacing: .08em;
    white-space: nowrap
}

/* Row titles render as typed ("Row 1"), not shouted like the surrounding
   sequencer labels. */
.sdp-seq-row-title,
.sgp-seq-row-title,
.hpp-seq-row-title {
    text-transform: none
}

.bws-steps-toggle {
    border: 1px solid var(--border-accent-light);
    border-radius: 3px;
    display: flex;
    overflow: hidden
}

.bws-steps-btn {
    background: 0 0;
    color: rgba(255,255,255,.6);
    border: 0;
    padding: 4px 14px;
    font-size: 13px;
    font-weight: 600;
    cursor: pointer;
    transition: background .15s,color .15s
}

.bws-steps-btn.active {
    background: var(--color-accent);
    color: #000
}

.bws-steps-btn:not(.active):hover {
    background: rgba(255,216,75,.12);
    color: var(--color-accent)
}

.bws-sequencer-grid {
    display: flex;
    flex-wrap: wrap;
    gap: 16px
}

.bws-seq-beat-group {
    display: flex;
    flex-direction: column;
    gap: 4px;
    flex: 1;
    min-width: 0
}

.bws-toggle-btn {
    height: auto;
    transition: all .2s ease;
    border-radius: 4px;
    border: 1px solid var(--border-accent-light);
    background: var(--bg-dark-light);
    cursor: pointer;
    padding: .4rem .75rem;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    font-size: 11px;
    color: var(--text-muted);
    white-space: nowrap;
    flex-shrink: 0
}

.bws-toggle-btn:hover {
    border-color: var(--color-accent);
    box-shadow: 0 0 6px var(--border-accent-light);
    color: var(--color-accent)
}

.bws-toggle-btn.active {
    background: var(--border-accent-light);
    border-color: var(--color-accent)!important;
    color: var(--color-accent)!important;
    box-shadow: 0 0 8px var(--glow-accent)
}

@media (max-width:600px) {
    .bws-controls-grid {
        flex-direction: column
    }

    .bws-controls-right {
        width: 100%
    }
}

.sdp-future-group,
.sdp-rhythm-group,
.hpp-control-group,
.hpp-visual-group {
    border: 1px solid var(--border-accent-light);
    border-radius: 4px
}

.sdp-future-group {
    min-height: 420px;
    background: #000;
    overflow: hidden;
    display: flex;
    flex-direction: column
}

.sdp-rhythm-group {
    flex: 1;
    padding: 1rem;
    background: var(--bg-dark-heavy)
}

.sdp-drum-ripple {
    position: absolute;
    border-radius: 50%;
    pointer-events: none;
    width: 32px;
    height: 32px;
    border: 2px solid rgba(255,216,75,.75);
    transform: translate(-50%,-50%) scale(0);
    animation: sdp-ripple-out 1.4s ease-out forwards
}

.sdp-charge-indicator,.sdp-drum-hint {
    position: absolute;
    pointer-events: none;
    transform: translateX(-50%)
}

.sdp-drum-hint {
    bottom: 1rem;
    left: 50%;
    z-index: 4;
    background: rgba(0,0,0,.55);
    color: var(--text-muted);
    font-size: 11px;
    padding: 2px 10px;
    border-radius: 10px;
    white-space: nowrap
}

.sdp-charge-indicator {
    width: 35px;
    height: 10px;
    border: 1px solid #ffd84b;
    background: 0 0;
    z-index: 10;
    box-sizing: border-box
}

.sdp-feel-group {
    padding: 1rem;
    background: var(--bg-dark-heavy);
    border: 1px solid var(--border-accent-light);
    border-radius: 4px;
    flex: 1
}

.sdp-seq-beat-steps,
.sgp-seq-beat-steps,
.sgp-seq-beat-pickers,
.sgp-sound-picker,
.hpp-seq-beat-steps,
.hpp-note-picker,
.hpp-seq-beat-pickers {
    display: flex;
    gap: 3px
}

.sdp-step,
.sgp-step,
.hpp-step {
    flex: 1;
    height: 48px;
    border: 1px solid rgba(255,216,75,.22);
    border-radius: 3px;
    cursor: pointer;
    position: relative;
    background: rgba(0,0,0,.35);
    overflow: hidden;
    padding: 0;
    transition: border-color .1s,box-shadow .1s;
    min-width: 0
}

.sdp-step::before,
.sgp-step::before {
    content: "";
    position: absolute;
    bottom: 0;
    left: 0;
    right: 0;
    height: var(--vel-h, 0%);
    background: var(--color-accent);
    transition: height .08s ease
}

.sdp-step[data-vel="-1"]::before,
.sgp-step[data-vel="-1"]::before {
    height: 60%;
    background: rgba(220,60,60,.85)
}

.sdp-step[data-vel="-1"] {
    border-color: rgba(220,60,60,.5)
}

.sdp-step[data-sound=drum-swipe]::before {
    background: rgba(70,160,255,.85)
}

.sdp-step[data-sound=drum-swipe] {
    border-color: rgba(70,160,255,.5)
}

.sdp-step[data-sound=finger-tap]::before {
    background: rgba(170,90,240,.85)
}

.sdp-step[data-sound=finger-tap] {
    border-color: rgba(170,90,240,.5)
}

.sdp-step[data-sound=strong-hit]::before {
    background: rgba(60,220,110,.85)
}

.sdp-step[data-sound=strong-hit] {
    border-color: rgba(60,220,110,.5)
}

.sdp-step[data-sound=frame-hit]::before {
    background: rgba(255,255,255,.75)
}

.sdp-step[data-sound=frame-hit] {
    border-color: rgba(255,255,255,.4)
}

.sdp-step[data-sound=muted-tap]::before {
    background: rgba(220,60,60,.85)
}

.sdp-step[data-sound=muted-tap] {
    border-color: rgba(220,60,60,.5)
}

.sdp-step.playing {
    border-color: #ffd84b;
    box-shadow: 0 0 0 2px rgba(255,216,75,.9),inset 0 0 6px rgba(255,216,75,.15)
}

.sdp-step:hover {
    border-color: rgba(255,216,75,.5)
}

.sdp-row-section {
    position: relative;
    margin: 0 1rem 38px;
    padding: 1rem;
    background: var(--bg-dark-heavy);
    border: 1px solid var(--border-accent-light);
    border-radius: 4px
}

#hpp-seq-rows-container>.hpp-sequencer-section:last-child,#sdp-rows-container>.sdp-row-section:last-child,#sgp-rows-container>.sgp-row-section:last-child {
    margin-bottom: 52px
}

/* .sdp-insert-row-btn / -dup-row-btn: see the shared row-action rules below. */

/* ── Row actions: insert a blank row / duplicate this row ──────────────────
   One centred pair hanging off the bottom edge of a row section. Both are the
   accent yellow with a black glyph — the green "+" they replace was the only
   green control left in the builders. */
.hpp-insert-row-actions,
.sdp-insert-row-actions,
.sgp-insert-row-actions,
.ft-insert-row-actions,
.bt-insert-row-actions,
.it-insert-row-actions {
    position: absolute;
    left: 50%;
    bottom: -13px;
    transform: translateX(-50%);
    display: flex;
    align-items: center;
    gap: 8px;
    z-index: 2;
}
.hpp-insert-row-btn, .hpp-dup-row-btn,
.sdp-insert-row-btn, .sdp-dup-row-btn,
.sgp-insert-row-btn, .sgp-dup-row-btn,
.ft-insert-row-btn, .ft-dup-row-btn,
.bt-insert-row-btn, .bt-dup-row-btn,
.it-insert-row-btn, .it-dup-row-btn {
    width: 26px;
    height: 26px;
    padding: 0;
    border: 0;
    border-radius: 50%;
    cursor: pointer;
    display: flex;
    align-items: center;
    justify-content: center;
    /* Same idle/hover pair as .bws-card-create-btn and .bws-info-badge: a darker
       yellow at rest that brightens to the accent on hover. Colour is the only
       thing that moves — no scale, no glow — so the disc stays put on the row
       separator it straddles. */
    background-color: #d9b53c;
    color: #111;
    transition: background-color .2s ease;
}
.hpp-insert-row-btn:hover, .hpp-dup-row-btn:hover,
.sdp-insert-row-btn:hover, .sdp-dup-row-btn:hover,
.sgp-insert-row-btn:hover, .sgp-dup-row-btn:hover,
.ft-insert-row-btn:hover:not(:disabled), .ft-dup-row-btn:hover:not(:disabled),
.bt-insert-row-btn:hover:not(:disabled), .bt-dup-row-btn:hover:not(:disabled),
.it-insert-row-btn:hover:not(:disabled), .it-dup-row-btn:hover:not(:disabled) {
    background: var(--color-accent, #ffd84b);
}
.hpp-insert-row-btn svg, .sdp-insert-row-btn svg, .sgp-insert-row-btn svg,
.ft-insert-row-btn svg, .bt-insert-row-btn svg, .it-insert-row-btn svg {
    width: 10px;
    height: 10px
}
.hpp-dup-row-btn svg, .sdp-dup-row-btn svg, .sgp-dup-row-btn svg,
.ft-dup-row-btn svg, .bt-dup-row-btn svg, .it-dup-row-btn svg {
    width: 13px;
    height: 13px
}
/* The timer modules gate their add/duplicate pairs until every row is populated.
   These buttons straddle a row separator / card border, so the dimmed state must
   stay fully opaque — `opacity` would let the line show through the disc. Both
   colours therefore flatten to the same solid grey when disabled. */
.ft-insert-row-btn:disabled, .ft-dup-row-btn:disabled,
.ft-add-timer-btn:disabled, .ft-dup-timer-btn:disabled, .ft-pause-timer-btn:disabled,
.bt-insert-row-btn:disabled, .bt-dup-row-btn:disabled,
.bt-add-timer-btn:disabled, .bt-dup-timer-btn:disabled, .bt-pause-timer-btn:disabled,
.it-insert-row-btn:disabled, .it-dup-row-btn:disabled,
.it-add-timer-btn:disabled, .it-dup-timer-btn:disabled, .it-pause-timer-btn:disabled {
    cursor: not-allowed;
    box-shadow: none;
    background: #6b6b6b;
    color: #2b2b2b;
}

/* The last row's actions are the "add another row" affordance for the whole
   composition, so they are drawn larger and hang further below the section. */
#hpp-seq-rows-container>.hpp-sequencer-section:last-child .hpp-insert-row-actions,
#sdp-rows-container>.sdp-row-section:last-child .sdp-insert-row-actions,
#sgp-rows-container>.sgp-row-section:last-child .sgp-insert-row-actions { bottom: -19px; }
#hpp-seq-rows-container>.hpp-sequencer-section:last-child .hpp-insert-row-btn,
#hpp-seq-rows-container>.hpp-sequencer-section:last-child .hpp-dup-row-btn,
#sdp-rows-container>.sdp-row-section:last-child .sdp-insert-row-btn,
#sdp-rows-container>.sdp-row-section:last-child .sdp-dup-row-btn,
#sgp-rows-container>.sgp-row-section:last-child .sgp-insert-row-btn,
#sgp-rows-container>.sgp-row-section:last-child .sgp-dup-row-btn {
    width: 38px;
    height: 38px;
}

#hpp-seq-rows-container>.hpp-sequencer-section:last-child .hpp-insert-row-btn svg,
#sdp-rows-container>.sdp-row-section:last-child .sdp-insert-row-btn svg,
#sgp-rows-container>.sgp-row-section:last-child .sgp-insert-row-btn svg {
    width: 14px;
    height: 14px
}
#hpp-seq-rows-container>.hpp-sequencer-section:last-child .hpp-dup-row-btn svg,
#sdp-rows-container>.sdp-row-section:last-child .sdp-dup-row-btn svg,
#sgp-rows-container>.sgp-row-section:last-child .sgp-dup-row-btn svg {
    width: 17px;
    height: 17px
}

/* ── Composition sections (drum) ──────────────────────────────────────────── */
.sdp-comp-section,
.sgp-comp-section,
.hpp-comp-section {
    position: relative;
    margin: 0 .5rem 50px;
    padding: .5rem .5rem .85rem;
    background: var(--bg-dark-light, #141414);
    /* Same edge as a timer card in the frequency-timers module: the muted
       accent border, no glow. */
    border: 1px solid var(--border-accent-light);
    border-radius: 8px
}
#sdp-rows-container>.sdp-comp-section:last-child { margin-bottom: 64px }

.sdp-comp-section-header,
.sgp-comp-section-header,
.hpp-comp-section-header {
    display: flex;
    align-items: center;
    gap: 8px;
    flex-wrap: wrap;
    padding: 6px 42px 10px 8px
}
.sdp-section-name-wrap,
.sdp-section-repeat-wrap,
.sgp-section-name-wrap,
.hpp-section-name-wrap,
.sgp-section-repeat-wrap,
.hpp-section-repeat-wrap { display: inline-flex; align-items: center; gap: 6px }
/* Sentence case, not uppercase — the name is user-editable, so it must render
   exactly as typed ("Section 1", "Intro", "Chorus B"). */
.sdp-section-name,
.sgp-section-name,
.hpp-section-name {
    font-weight: 700;
    font-size: 14px;
    letter-spacing: .04em;
    color: var(--color-accent, #ffd84b)
}
.sdp-section-name-input,
.sgp-section-name-input,
.hpp-section-name-input,
.bws-nav-name-input {
    font: inherit;
    font-weight: 700;
    color: #fff;
    background: #000;
    border: 1px solid var(--color-accent, #ffd84b);
    border-radius: 4px;
    padding: 2px 6px;
    min-width: 120px
}
/* The section loop button is deliberately absent here — it is styled with the
   row loop buttons (green) further down, so both loops read as one control. */
.sdp-section-rename-btn,
.sdp-section-copy-btn,
.sdp-section-paste-btn,
.sgp-section-rename-btn,
.sgp-section-copy-btn,
.sgp-section-paste-btn,
.hpp-section-rename-btn,
.hpp-section-copy-btn,
.hpp-section-paste-btn {
    width: 28px;
    height: 28px;
    padding: 0;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    background: transparent;
    border: 1px solid var(--border-accent-light, rgba(255,216,75,.25));
    border-radius: 5px;
    color: var(--text-muted, #aaa);
    cursor: pointer;
    transition: color .15s,border-color .15s,background .15s
}
.sdp-section-rename-btn:hover,
.sdp-section-copy-btn:hover,
.sdp-section-paste-btn:hover,
.sdp-section-copy-btn.sdp-copy-active,
.sgp-section-rename-btn:hover,
.sgp-section-copy-btn:hover,
.sgp-section-paste-btn:hover,
.hpp-section-rename-btn:hover,
.hpp-section-copy-btn:hover,
.hpp-section-paste-btn:hover,
.sgp-section-copy-btn.sgp-copy-active,
.hpp-section-copy-btn.hpp-copy-active {
    color: var(--color-accent, #ffd84b);
    border-color: var(--color-accent, #ffd84b)
}
.sdp-section-paste-btn.sdp-paste-ready,
.sgp-section-paste-btn.sgp-paste-ready,
.hpp-section-paste-btn.hpp-paste-ready { color: #28a745; border-color: #28a745 }
.sdp-section-header-spacer { flex: 1 1 auto }
/* The section Repeat field is styled with the row Repeat field — see the shared
   .*-repetition-input rule below. */
.sdp-section-repeat-input:disabled { opacity: .4 }

.sdp-comp-section.sdp-section-collapsed .sdp-comp-section-rows { display: none }

.sdp-section-remove-btn,
.sgp-section-remove-btn,
.hpp-section-remove-btn {
    position: absolute;
    top: -12px;
    right: -12px;
    width: 26px;
    height: 26px;
    display: flex;
    align-items: center;
    justify-content: center;
    border: 0;
    border-radius: 50%;
    background: #d9342b;
    color: #fff;
    font-size: 18px;
    line-height: 1;
    cursor: pointer;
    box-shadow: 0 2px 6px rgba(217,52,43,.5);
    transition: transform .15s,background .15s;
    z-index: 3
}
.sdp-section-remove-btn:hover,
.sgp-section-remove-btn:hover,
.hpp-section-remove-btn:hover { transform: scale(1.15); background: #ff4d42 }
.sdp-section-remove-btn:disabled,
.sgp-section-remove-btn:disabled,
.hpp-section-remove-btn:disabled { opacity: .35; cursor: default }

/* ── Section actions: add a new section / duplicate this one ───────────────
   Same centred pair as the row actions, one level up: green rather than the
   accent yellow, so "acts on the whole section" reads apart from "acts on this
   row" at a glance. */
.sdp-add-section-actions,
.sgp-add-section-actions,
.hpp-add-section-actions,
.ft-add-timer-actions,
.bt-add-timer-actions,
.it-add-timer-actions {
    position: absolute;
    left: 50%;
    bottom: -13px;
    transform: translateX(-50%);
    display: flex;
    align-items: center;
    gap: 8px;
    z-index: 2;
}
.sdp-add-section-btn, .sdp-dup-section-btn,
.sgp-add-section-btn, .sgp-dup-section-btn,
.hpp-add-section-btn, .hpp-dup-section-btn,
.ft-add-timer-btn, .ft-dup-timer-btn, .ft-pause-timer-btn,
.bt-add-timer-btn, .bt-dup-timer-btn, .bt-pause-timer-btn,
.it-add-timer-btn, .it-dup-timer-btn, .it-pause-timer-btn {
    width: 26px;
    height: 26px;
    padding: 0;
    display: flex;
    align-items: center;
    justify-content: center;
    border: 0;
    border-radius: 50%;
    /* Green rather than yellow — the colour is what says "acts on the section /
       timer" instead of "acts on this row". Behaves like the yellow pair
       otherwise: darker at rest, lighter on hover, and colour is the only thing
       that moves — no scale, no glow. */
    background: #28a745;
    color: #fff;
    cursor: pointer;
    transition: background-color .2s ease;
}
.sdp-add-section-btn:hover, .sdp-dup-section-btn:hover,
.sgp-add-section-btn:hover, .sgp-dup-section-btn:hover,
.hpp-add-section-btn:hover, .hpp-dup-section-btn:hover,
.ft-add-timer-btn:hover:not(:disabled), .ft-dup-timer-btn:hover:not(:disabled), .ft-pause-timer-btn:hover:not(:disabled),
.bt-add-timer-btn:hover:not(:disabled), .bt-dup-timer-btn:hover:not(:disabled), .bt-pause-timer-btn:hover:not(:disabled),
.it-add-timer-btn:hover:not(:disabled), .it-dup-timer-btn:hover:not(:disabled), .it-pause-timer-btn:hover:not(:disabled) {
    background: #34c759;
}
/* Third disc in the timer modules: drop a silent gap after this card. */
.ft-pause-timer-btn svg,
.bt-pause-timer-btn svg,
.it-pause-timer-btn svg { width: 11px; height: 11px }
/* Glyphs match the yellow row pair exactly — the colour alone carries the
   "acts on the section/timer" vs "acts on this row" distinction. */
.sdp-add-section-btn svg,
.sgp-add-section-btn svg,
.hpp-add-section-btn svg,
.ft-add-timer-btn svg,
.bt-add-timer-btn svg,
.it-add-timer-btn svg { width: 10px; height: 10px }
.sdp-dup-section-btn svg,
.sgp-dup-section-btn svg,
.hpp-dup-section-btn svg,
.ft-dup-timer-btn svg,
.bt-dup-timer-btn svg,
.it-dup-timer-btn svg { width: 13px; height: 13px }

/* Lead insert bar — the same discs mirrored onto the FIRST card's top border, the
   one slot the per-card bars can't reach (they all insert after their own card). */
.ft-lead-timer-actions,
.bt-lead-timer-actions,
.it-lead-timer-actions {
    position: absolute;
    left: 50%;
    top: -13px;
    transform: translateX(-50%);
    display: flex;
    align-items: center;
    gap: 8px;
    z-index: 2;
}

/* ── Section navigator (fixed, top-right) ─────────────────────────────────── */
:root {
    --bws-nav-top: 90px;   /* distance from the viewport top; also the height calc's origin */
    --bws-nav-gap: 12px;   /* breathing room between the navigator and Now Playing */
    /* Width of the right-edge floating panels. The section navigator and Now Playing
       stack as one column down that edge, so they have to match — a token rather than
       two numbers and a comment asking the next person to keep them in step.
       #mr-now-playing reads it too, from the <style> the recorder injects. */
    --bws-float-panel-w: 300px;
    /* This panel is content-box — there is no universal box-sizing reset in this
       stylesheet — so max-height sizes the CONTENT and the border sits OUTSIDE it.
       Anything working out where the panel actually ends has to add it back. The
       padding now lives on the inner .bws-nav-scroll, inside the measured content
       box, so only the 1px border top and bottom counts. */
    --bws-nav-chrome: 2px;
}
.bws-section-navigator {
    position: fixed;
    top: var(--bws-nav-top);
    right: 14px;
    /* Shares --bws-float-panel-w with the Now Playing panel so the two line up as
       one column down the right edge. */
    width: var(--bws-float-panel-w, 300px);
    /* Both panels are fixed to the right edge — this one hangs from the top, the
       Now Playing panel sits on the bottom — so a tall navigator and a full
       Now Playing list used to run into each other, and Now Playing won (z-index
       99990 vs 9000): the bottom of the section list disappeared behind it.
       The navigator now stops short of whatever the other panel actually
       occupies. --bws-np-top is the Now Playing panel's live top edge, published
       by MEDIA-RECORDER-MODULE-PRO/js/media-recorder.js, so collapsing that panel
       to its pill or running a short channel list gives the height straight back.
       The 100vh fallback is for pages with no Now Playing panel at all (a module
       opened standalone), where there is nothing to avoid and min() simply keeps
       the 62vh ceiling. */
    max-height: min(62vh, calc(var(--bws-np-top, 100vh) - var(--bws-nav-top) - var(--bws-nav-gap) - var(--bws-nav-chrome)));
    /* Chrome only. .bws-nav-scroll inside does the scrolling, and overflow:hidden
       clips its scrollbar to this radius so the border is never cut. */
    display: flex;
    flex-direction: column;
    overflow: hidden;
    background: rgba(15,15,15,.78);
    backdrop-filter: blur(14px) saturate(1.15);
    -webkit-backdrop-filter: blur(14px) saturate(1.15);
    border: 1px solid var(--color-accent, #ffd84b);
    border-radius: 8px;
    box-shadow: 0 6px 24px rgba(0,0,0,.55);
    z-index: 9000;
    opacity: 0;
    transform: translateX(12px);
    pointer-events: none;
    transition: opacity .25s ease,transform .25s ease
}
.bws-section-navigator.bws-nav-visible { opacity: 1; transform: translateX(0); pointer-events: auto }
/* Carries the padding the panel used to have, so the scrollbar still sits hard
   against the panel edge and simply gets clipped by its border-radius. Only this
   middle row scrolls; the title and the transport are pinned flex rows either
   side of it, which is why neither needs position:sticky the way the Now Playing
   panel's do (there, everything shares one scroller). */
.bws-nav-scroll {
    flex: 1 1 auto;
    min-height: 0;
    overflow-y: auto;
    padding: 10px;
}
.bws-nav-head {
    position: relative;
    flex: 0 0 auto;
    min-height: 30px;
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 10px 10px 7px;
    border-bottom: 1px solid var(--border-accent-light, rgba(255,216,75,.2));
}
.bws-nav-head .bws-nav-title { margin-bottom: 0 }
/* Caret button chrome. Same treatment as the Now Playing panel's minimize button,
   and shared by both places a caret toggles a disclosure: the navigator header's
   expand-all / collapse-all pair, and each section's collapse toggle down in the
   builder. Positioning is deliberately NOT in here — the header pair is absolutely
   placed so the title stays centred on the panel rather than on whatever space the
   buttons leave, while the builder's sits inline in the section header. */
.bws-caret-btn {
    width: 30px;
    height: 30px;
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 0;
    border: 1px solid var(--border-accent-light, rgba(255,216,75,.35));
    border-radius: 6px;
    background: var(--bg-dark-light, #1a1a1a);
    color: var(--color-accent, #ffd84b);
    cursor: pointer;
    transition: background .15s, color .15s;
}
.bws-caret-btn:hover { background: var(--color-accent, #ffd84b); color: #111 }
.bws-caret-btn svg { display: block; width: 18px; height: 18px; fill: currentColor }
.bws-nav-hbtn-l,
.bws-nav-hbtn-r { position: absolute; top: 10px }
.bws-nav-hbtn-l { left: 10px }
.bws-nav-hbtn-r { right: 10px }

/* ── Navigator transport ──────────────────────────────────────────────────
   Same shape as the Now Playing panel's master transport: a centred readout, the
   position slider, then a row of round buttons. It owns no playback state — the
   buttons click the module's own REW / PLAY / FF and the slider dispatches on the
   module's own seek, so ⏮ / ⏭ step sections here exactly as they do down in the
   module, including the 3-second rewind grace and the disabled-while-looping rule. */
.bws-nav-foot {
    flex: 0 0 auto;
    display: flex;
    flex-direction: column;
    gap: 8px;
    padding: 8px 10px 10px;
    border-top: 1px solid var(--border-accent-light, rgba(255,216,75,.2));
}
.bws-nav-seek { display: flex; flex-direction: column; gap: 4px }
.bws-nav-time {
    text-align: center;
    font-size: 11px;
    font-weight: 700;
    letter-spacing: .04em;
    font-variant-numeric: tabular-nums;
    color: var(--color-accent, #ffd84b);
}
/* The slider rides above the section bars on z-index with a transparent track, so
   the breaks between sections read through it and the thumb stays grabbable. */
.bws-nav-seek-wrap { position: relative; display: flex; align-items: center }
.bws-nav-segs {
    position: absolute; left: 0; right: 0; top: 50%;
    height: 4px; transform: translateY(-50%);
    display: flex; align-items: center; gap: 1px;
    pointer-events: none;
}
.bws-nav-segs .bws-seg { height: 4px; border-radius: 2px; min-width: 2px }
.bws-nav-pos {
    -webkit-appearance: none; appearance: none;
    position: relative; z-index: 1;
    flex: 1 1 auto; min-width: 0;
    height: 4px; border-radius: 2px; cursor: pointer;
    background: transparent;
}
.bws-nav-pos::-webkit-slider-thumb {
    -webkit-appearance: none; appearance: none;
    width: 10px; height: 10px; border-radius: 50%;
    background: var(--color-accent, #ffd84b); border: none;
}
.bws-nav-pos::-moz-range-thumb {
    width: 10px; height: 10px; border-radius: 50%;
    background: var(--color-accent, #ffd84b); border: none; cursor: pointer;
}
.bws-nav-pos::-moz-range-track { height: 4px; border-radius: 2px; background: transparent }
/* Nothing to seek along (a looping row or section): the whole track greys out,
   matching what the module does to its own bar. */
.bws-nav-pos.bws-nav-pos-off { opacity: .4; pointer-events: none }
.bws-nav-pos.bws-nav-pos-off ~ .bws-nav-segs { opacity: .4 }
.bws-nav-btns { display: flex; align-items: center; justify-content: center; gap: 14px }
.bws-nav-btn {
    width: 30px; height: 30px; padding: 0;
    display: flex; align-items: center; justify-content: center;
    border-radius: 50%;
    border: 1px solid var(--border-accent-light, rgba(255,216,75,.2));
    background: var(--bg-dark-light, #1a1a1a);
    color: var(--color-accent, #ffd84b);
    cursor: pointer;
}
.bws-nav-btn:hover:not(:disabled) { color: #111; background: var(--color-accent, #ffd84b) }
.bws-nav-btn:disabled { opacity: .4; cursor: not-allowed }
.bws-nav-btn svg { display: block }
.bws-nav-btn .bws-nav-ic-pause { display: none }
.bws-nav-btn.playing .bws-nav-ic-play { display: none }
.bws-nav-btn.playing .bws-nav-ic-pause { display: block }
/* Playing reads as a filled button, not an outlined one — the same inversion the
   button already uses on hover, so pause is unmistakable at a glance. */
.bws-nav-btn.playing {
    background: var(--color-accent, #ffd84b);
    border-color: var(--color-accent, #ffd84b);
    color: #111;
}
.bws-nav-title {
    margin-bottom: 8px;
    text-align: center;
    font-size: 11px;
    font-weight: 700;
    letter-spacing: .12em;
    color: var(--color-accent, #ffd84b)
}
.bws-nav-item {
    display: flex;
    align-items: center;
    gap: 5px;
    padding: 5px 6px;
    margin-bottom: 5px;
    background: var(--bg-dark-light, #1a1a1a);
    border: 1px solid var(--border-accent-light, rgba(255,216,75,.2));
    border-radius: 5px;
    cursor: grab
}
.bws-nav-item.bws-nav-dragging { opacity: .5 }
.bws-nav-item.bws-nav-dragover { border-color: var(--color-accent, #ffd84b); box-shadow: 0 0 0 1px var(--color-accent, #ffd84b) }
.bws-nav-item.bws-nav-playing { border-color: #28a745; box-shadow: 0 0 8px rgba(40,167,69,.5) }
.bws-nav-handle { color: var(--text-muted, #888); font-size: 12px; cursor: grab; user-select: none }
.bws-nav-name {
    flex: 1 1 auto;
    color: #eee;
    font-size: 12px;
    font-weight: 600;
    cursor: pointer;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis
}
.bws-nav-item.bws-nav-collapsed .bws-nav-name { color: var(--text-muted, #999); font-style: italic }
/* The row you last clicked. Same specificity as the collapsed rule above, so it
   has to stay below it to win on a section that is both. Deliberately not the
   same signal as .bws-nav-playing (green ring) — that one tracks what is
   sounding, this one tracks what you picked, and they are often different rows. */
.bws-nav-item.bws-nav-selected .bws-nav-name { color: var(--color-accent, #ffd84b) }
/* Delete: the shared X-button family (.bws-x-btn, see the grouped rules further
   down) carries the whole look, so only the size is set here — smaller than the
   Now Playing panel's 20px one so it stays inside the row's ~18px content height
   instead of stretching it. Scoped to .bws-nav-item so it outranks the family's
   single-class base rule, which sits later in this file. */
.bws-nav-item .bws-nav-del { width: 16px; height: 16px; background-size: 9px }
.bws-nav-expand,
.bws-nav-rename,
.bws-nav-dup {
    padding: 2px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    background: transparent;
    border: 0;
    color: var(--text-muted, #999);
    cursor: pointer
}
.bws-nav-expand:hover,.bws-nav-rename:hover,.bws-nav-dup:hover { color: var(--color-accent, #ffd84b) }
/* The collapsed state used to be a -90deg rotation of a single down caret. It now
   swaps to a real up caret instead, so the row toggle and the header buttons share
   one vocabulary: down opens, up folds. */
.bws-nav-expand svg { display: block; width: 16px; height: 16px }
.bws-nav-name-input { min-width: 90px; flex: 1 1 auto }

/* Composition sections. The drum keeps its own copies of these below: its section
   BODIES still use sdp-* classes, unlike the navigator, which all three share. */
#sgp-rows-container>.sgp-comp-section:last-child,#hpp-seq-rows-container>.hpp-comp-section:last-child { margin-bottom: 64px }

.sgp-section-header-spacer,.hpp-section-header-spacer { flex: 1 1 auto }
.sgp-section-repeat-input:disabled,.hpp-section-repeat-input:disabled { opacity: .4 }

.sgp-comp-section.sgp-section-collapsed .sgp-comp-section-rows,.hpp-comp-section.hpp-section-collapsed .hpp-comp-section-rows { display: none }

.sdp-seq-beat-pickers {
    display: flex;
    gap: 3px;
    margin-top: 2px
}

.sdp-sound-picker {
    flex: 1;
    min-width: 0;
    cursor: default;
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 3px;
    position: relative;
    z-index: 1;
    transform-origin: center center;
    transition: transform .18s ease;
    will-change: transform
}

.sdp-steps-16 .sdp-sound-picker:hover {
    transform: scale(1.7);
    z-index: 10
}

.sdp-sound-picker-svg,
.sgp-sound-picker-svg {
    width: 100%;
    aspect-ratio: 1;
    display: block
}

.sdp-step-btn-bar,
.sgp-step-btn-bar,
.hpp-step-btn-bar {
    display: flex;
    justify-content: center;
    gap: 3px;
    width: 100%;
    opacity: 0;
    transition: opacity .15s ease
}

.sdp-sound-picker:hover .sdp-step-btn-bar {
    opacity: 1
}

.sdp-step-paste-btn,
.sdp-seg-paste-btn,
.sgp-step-paste-btn,
.sgp-seg-paste-btn,
.hpp-seg-paste-btn,
.hpp-step-paste-btn {
    opacity: .3;
    pointer-events: none
}

.sdp-step-paste-btn.hpp-paste-ready,
.sgp-step-paste-btn.hpp-paste-ready,
.hpp-seg-paste-btn.hpp-paste-ready,
.hpp-step-paste-btn.hpp-paste-ready {
    opacity: 1;
    pointer-events: auto
}

.sdp-seq-row-header,
.sgp-seq-row-header {
    display: flex;
    justify-content: space-between;
    align-items: center;
    margin-bottom: .75rem;
    gap: 10px;
    flex-wrap: wrap;
    width: 100%
}

.sdp-seq-row-left,
.sgp-seq-row-left,
.hpp-seq-row-left {
    display: flex;
    align-items: center;
    gap: 6px;
    flex-shrink: 0
}

.sdp-row-copy-btn,
.sdp-row-loop-btn,
.sdp-row-paste-btn,
.sdp-row-reset-btn,
.sgp-row-copy-btn,
.sgp-row-loop-btn,
.sgp-row-paste-btn,
.sgp-row-reset-btn,
.hpp-row-copy-btn,
.hpp-row-loop-btn,
.hpp-row-paste-btn,
.hpp-row-reset-btn,
.sdp-section-loop-btn,
.sgp-section-loop-btn,
.hpp-section-loop-btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 28px;
    height: 28px;
    padding: 0;
    border-radius: 4px;
    cursor: pointer;
    flex-shrink: 0;
    transition: color .15s,background .15s,border-color .15s,transform .1s
}

/* Section loops are styled identically to row loops — same green, same glyph,
   same slot in front of the title; only the scope differs. */
.sdp-row-loop-btn svg,
.sgp-row-loop-btn svg,
.hpp-row-loop-btn svg,
.sdp-section-loop-btn svg,
.sgp-section-loop-btn svg,
.hpp-section-loop-btn svg {
    width: 15px;
    height: 15px
}

.sdp-row-loop-btn,
.sgp-row-loop-btn,
.hpp-row-loop-btn,
.sdp-section-loop-btn,
.sgp-section-loop-btn,
.hpp-section-loop-btn {
    border: 1px solid rgba(60,200,100,.45);
    background: rgba(60,200,100,.1);
    color: rgba(60,200,100,.75)
}

.sdp-row-loop-btn:hover,
.sgp-row-loop-btn:hover,
.hpp-row-loop-btn:hover,
.sdp-section-loop-btn:hover,
.sgp-section-loop-btn:hover,
.hpp-section-loop-btn:hover {
    color: #3cdc6e;
    background: rgba(60,200,100,.2);
    border-color: rgba(60,200,100,.8);
    transform: scale(1.1)
}

.sdp-row-loop-btn.sdp-loop-active,
.sgp-row-loop-btn.sgp-loop-active,
.hpp-row-loop-btn.hpp-loop-active,
.sdp-section-loop-btn.sdp-loop-active,
.sgp-section-loop-btn.sgp-loop-active,
.hpp-section-loop-btn.hpp-loop-active {
    color: #3ce66e!important;
    background: rgba(60,200,100,.28)!important;
    border-color: #3cdc6e!important;
    box-shadow: 0 0 6px rgba(60,220,110,.5)
}

.sdp-row-copy-btn,
.sdp-row-paste-btn,
.sdp-row-reset-btn,
.sgp-row-copy-btn,
.sgp-row-paste-btn,
.sgp-row-reset-btn,
.hpp-row-copy-btn,
.hpp-row-paste-btn,
.hpp-row-reset-btn {
    border: 1px solid rgba(255,216,75,.45);
    background: #1a1608
}

.sdp-row-copy-btn,.sdp-row-paste-btn {
    color: rgba(255,216,75,.75)
}

.sdp-row-reset-btn,
.hpp-step-reset-btn,
.sgp-row-reset-btn,
.hpp-row-reset-btn {
    color: rgba(220,60,60,.75);
    border-color: rgba(220,60,60,.4);
    background: #120505
}

.sdp-row-reset-btn:hover,
.sgp-row-reset-btn:hover,
.hpp-row-reset-btn:hover {
    color: #dc3c3c;
    background: #210909;
    border-color: rgba(220,60,60,.6);
    transform: scale(1.1)
}

.sdp-row-copy-btn:hover,.sdp-row-paste-btn.sdp-paste-ready:hover {
    color: var(--color-accent);
    background: #26200b;
    border-color: rgba(255,216,75,.55);
    transform: scale(1.1)
}

.sdp-row-paste-btn,
.sgp-row-paste-btn,
.hpp-row-paste-btn {
    opacity: .35;
    pointer-events: none
}

.sdp-row-paste-btn.sdp-paste-ready {
    opacity: 1;
    pointer-events: auto;
    color: rgba(255,216,75,.6)
}

.sdp-seq-row-controls,
.sgp-seq-row-controls,
.hpp-seq-row-controls {
    display: flex;
    align-items: center;
    gap: 10px;
    flex-shrink: 0;
    flex-wrap: wrap;
    margin-right: 24px
}

/* Row Repeat and section Repeat are the same control at two scopes, so they
   share one look (and the "Repeat" label already shares .bws-seq-label). */
.sdp-repetition-input,
.sgp-repetition-input,
.hpp-repetition-input,
.sdp-section-repeat-input,
.sgp-section-repeat-input,
.hpp-section-repeat-input {
    width: 46px;
    background: var(--bg-dark, #1a1a1a);
    color: var(--color-text, #fff);
    border: 1px solid var(--border-accent-light);
    border-radius: 3px;
    padding: 3px 6px;
    font-size: 13px;
    text-align: center;
    outline: 0;
    -moz-appearance: textfield
}

.sdp-repetition-input::-webkit-inner-spin-button,
.sdp-repetition-input::-webkit-outer-spin-button,
.sgp-repetition-input::-webkit-inner-spin-button,
.sgp-repetition-input::-webkit-outer-spin-button,
.hpp-repetition-input::-webkit-inner-spin-button,
.hpp-repetition-input::-webkit-outer-spin-button,
.sdp-section-repeat-input::-webkit-inner-spin-button,
.sdp-section-repeat-input::-webkit-outer-spin-button,
.sgp-section-repeat-input::-webkit-inner-spin-button,
.sgp-section-repeat-input::-webkit-outer-spin-button,
.hpp-section-repeat-input::-webkit-inner-spin-button,
.hpp-section-repeat-input::-webkit-outer-spin-button {
    -webkit-appearance: none;
    margin: 0
}

.sdp-seg-header,
.sgp-seg-header,
.hpp-seg-header,
.mex-dur-hms,
.sbp-dur-hms {
    display: flex;
    align-items: center;
    gap: 4px
}

/* Segment titles render as typed ("Segment 1") — the beat-number class they
   share with the step ruler is uppercased. */
.sdp-seg-header .bws-seq-beat-num,
.sgp-seg-header .bws-seq-beat-num,
.hpp-seg-header .bws-seq-beat-num {
    text-transform: none
}

.sdp-seg-copy-btn,
.sdp-seg-paste-btn,
.sdp-seg-reset-btn,
.sgp-seg-copy-btn,
.sgp-seg-paste-btn,
.sgp-seg-reset-btn,
.hpp-seg-copy-btn,
.hpp-seg-paste-btn,
.hpp-seg-reset-btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 24px;
    height: 24px;
    border-radius: 3px;
    border: 1px solid rgba(255,216,75,.4);
    background: #1a1608;
    cursor: pointer;
    flex-shrink: 0;
    padding: 0;
    transition: color .15s,background .15s,border-color .15s
}

.sdp-seg-copy-btn,.sdp-seg-paste-btn {
    color: rgba(255,216,75,.7)
}

.sdp-seg-reset-btn {
    color: rgba(220,60,60,.75);
    border-color: rgba(220,60,60,.4);
    background: #120505
}

.sdp-seg-reset-btn:hover {
    color: #dc3c3c;
    background: #210909;
    border-color: rgba(220,60,60,.6)
}

.sdp-seg-copy-btn:hover,.sdp-seg-paste-btn.sdp-paste-ready:hover {
    color: var(--color-accent);
    background: #26200b;
    border-color: rgba(255,216,75,.5)
}

.sdp-seg-paste-btn.sdp-paste-ready {
    opacity: 1;
    pointer-events: auto
}

.sdp-copy-active,
.sgp-copy-active,
.hpp-copy-active {
    color: var(--color-accent)!important;
    background: #383011!important;
    border-color: rgba(255,216,75,.75)!important;
    opacity: 1!important
}

@media (max-width:600px) {
    .bws-sequencer-grid {
        gap: 4px
    }

    .sdp-seq-beat-steps {
        gap: 2px
    }

    .sdp-step {
        height: 40px
    }
}

.sgp-step[data-vel="-1"] {
    border-color: rgba(220,60,60,.5)
}

.sgp-step[data-sound=whale-song]::before {
    background: rgba(70,160,255,.85)
}

.sgp-step[data-sound=whale-song] {
    border-color: rgba(70,160,255,.5)
}

.sgp-step[data-sound=muted-thud]::before {
    background: rgba(170,90,240,.85)
}

.sgp-step[data-sound=muted-thud] {
    border-color: rgba(170,90,240,.5)
}

.sgp-step[data-sound=tamtam-wash]::before {
    background: rgba(60,220,110,.85)
}

.sgp-step[data-sound=tamtam-wash] {
    border-color: rgba(60,220,110,.5)
}

.sgp-step[data-sound=roll-swell]::before {
    background: rgba(255,255,255,.75)
}

.sgp-step[data-sound=roll-swell] {
    border-color: rgba(255,255,255,.4)
}

.sgp-step[data-sound=rim-tap]::before {
    background: rgba(220,60,60,.85)
}

.sgp-step[data-sound=rim-tap] {
    border-color: rgba(220,60,60,.5)
}

.sgp-step.playing {
    border-color: #ffd84b;
    box-shadow: 0 0 0 2px rgba(255,216,75,.9),inset 0 0 6px rgba(255,216,75,.15)
}

.sgp-step:hover {
    border-color: rgba(255,216,75,.5)
}

.sgp-sound-picker,
.hpp-note-picker {
    flex: 1;
    min-width: 0;
    cursor: default;
    flex-direction: column;
    align-items: center;
    position: relative;
    z-index: 1;
    transform-origin: center center;
    transition: transform .18s ease;
    will-change: transform
}

.sgp-steps-16 .sgp-sound-picker:hover {
    transform: scale(1.7);
    z-index: 10
}

.sgp-row-section {
    position: relative;
    margin: 0 1rem 38px;
    padding: 1rem;
    background: var(--bg-dark-heavy);
    border: 1px solid var(--border-accent-light);
    border-radius: 4px
}

/* .sgp-insert-row-btn / -dup-row-btn: see the shared row-action rules below. */

.sgp-sound-picker:hover .sgp-step-btn-bar {
    opacity: 1
}

.sgp-row-header-container {
    width: 100%
}

.sgp-row-copy-btn,.sgp-row-paste-btn {
    color: rgba(255,216,75,.75)
}

.sgp-row-copy-btn:hover,.sgp-row-paste-btn.sgp-paste-ready:hover {
    color: var(--color-accent);
    background: #26200b;
    border-color: rgba(255,216,75,.55);
    transform: scale(1.1)
}

.sgp-row-paste-btn.sgp-paste-ready {
    opacity: 1;
    pointer-events: auto;
    color: rgba(255,216,75,.6)
}

.sgp-seg-copy-btn,.sgp-seg-paste-btn {
    color: rgba(255,216,75,.7)
}

.sgp-seg-reset-btn {
    color: rgba(220,60,60,.75);
    border-color: rgba(220,60,60,.4);
    background: #120505
}

.sgp-seg-reset-btn:hover {
    color: #dc3c3c;
    background: #210909;
    border-color: rgba(220,60,60,.6)
}

.sgp-seg-copy-btn:hover,.sgp-seg-paste-btn.sgp-paste-ready:hover {
    color: var(--color-accent);
    background: #26200b;
    border-color: rgba(255,216,75,.5)
}

.sgp-seg-paste-btn.sgp-paste-ready {
    opacity: 1;
    pointer-events: auto
}

@media (max-width:600px) {
    .bws-sequencer-grid {
        gap: 4px
    }

    .sgp-seq-beat-pickers,.sgp-seq-beat-steps {
        gap: 2px
    }

    .sgp-step {
        height: 40px
    }
}

.hpp-preset-row {
    display: flex;
    gap: 1rem;
    padding: 0 1rem .75rem
}

.hpp-preset-col {
    display: flex;
    flex-direction: column;
    gap: .35rem;
    flex: 1
}

.hpp-preset-col--narrow {
    flex: 0 0 auto;
    min-width: 130px
}

/* Field label above a dropdown - shared by the handpan player's Type /
   Intonation / Tuning selectors and the drum player's Drum Type. */
.bws-field-label {
    font-size: 11px;
    font-weight: 700;
    color: var(--color-accent);
    text-transform: uppercase;
    letter-spacing: .08em
}

.hpp-control-group {
    background: var(--bg-dark-heavy);
    padding: 1rem
}

.hpp-visual-group {
    width: 100%;
    min-height: 680px;
    background: #000;
    overflow: hidden
}

.hpp-handpan-wrap {
    position: relative;
    width: 100%;
    height: 720px;
    cursor: crosshair;
    user-select: none;
    -webkit-user-select: none;
    overflow: hidden;
    background-image: url(../HANDPAN-PLAYER-PRO/assets/hang.webp);
    background-size: auto 680px;
    background-position: center 40px;
    background-repeat: no-repeat
}

/* Per-type background image — toggled by _loadType() */
.hpp-handpan-wrap.hpp-type-hang { background-image: url(../HANDPAN-PLAYER-PRO/assets/hang.webp); }
.hpp-handpan-wrap.hpp-type-pantam { background-image: url(../HANDPAN-PLAYER-PRO/assets/pantam.webp); }
.hpp-handpan-wrap.hpp-type-rav_vast { background-image: url(../HANDPAN-PLAYER-PRO/assets/rav-vast.webp); }
.hpp-handpan-wrap.hpp-type-ayasha { background-image: url(../HANDPAN-PLAYER-PRO/assets/ayasha.webp); }
.hpp-handpan-wrap.hpp-type-integral { background-image: url(../HANDPAN-PLAYER-PRO/assets/integral.webp); }
.hpp-handpan-wrap.hpp-type-kurd_dark { background-image: url(../HANDPAN-PLAYER-PRO/assets/kurd-dark.webp); }

.hpp-handpan-content {
    position: absolute;
    top: 40px;
    left: 0;
    right: 0;
    height: 680px;
    overflow: hidden
}

.hpp-charge-indicator {
    position: absolute;
    width: 35px;
    height: 10px;
    border: 1px solid #ffd84b;
    background: 0 0;
    pointer-events: none;
    z-index: 10;
    transform: translateX(-50%);
    box-sizing: border-box
}

.hpp-handpan-svg {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    display: block;
    z-index: 1;
    transition: filter .08s linear
}

.hpp-note-group {
    cursor: pointer
}

.hpp-note-group circle {
    transition: fill .12s ease
}

.hpp-note-group:hover>circle {
    fill: rgba(255,216,75,.2)!important
}

.hpp-note-label {
    font-family: inherit;
    font-size: 13px;
    font-weight: 700;
    fill: rgba(255,216,75,.85);
    pointer-events: none;
    user-select: none
}

.hpp-ding-label {
    font-size: 14px
}

.hpp-ripple,
.hpp-ripple-layer,
.hpp-hint,
.hpp-rec-countdown {
    position: absolute;
    pointer-events: none
}

.hpp-ripple-layer {
    inset: 0;
    z-index: 2;
    overflow: hidden
}

.hpp-ripple {
    width: 28px;
    height: 28px;
    border-radius: 50%;
    border: 2px solid rgba(255,216,75,.75);
    transform: translate(-50%,-50%) scale(0);
    animation: hpp-ripple-out 1.6s ease-out forwards
}

.hpp-recorder-bar {
    display: flex;
    flex-direction: column;
    gap: 6px;
    padding: 14px 18px 12px;
    background: var(--bg-dark-heavy);
    border: 1px solid var(--border-accent-light);
    border-radius: 4px;
    width: 100%;
    box-sizing: border-box
}

.hpp-recorder-bar .bws-group-header {
    margin-bottom: 0
}

.hpp-recorder-bar .bws-group-description {
    font-size: 11px;
    color: rgba(255,255,255,.55);
    line-height: 1.45;
    margin-bottom: 2px
}

.hpp-rec-controls {
    display: flex;
    align-items: center;
    gap: 8px;
    flex-wrap: wrap;
    /* Set the row off from the group description above it */
    margin-top: .75rem
}

/* Session Recorder field labels (Tempo / Time / Row) in the Gong, Handpan and
   Drum modules — same recipe as .sbp-ctrl-label and the sticky bar labels. */
.hpp-rec-label {
    font-size: 11px;
    font-weight: 700;
    letter-spacing: .08em;
    color: var(--text-muted, #aaa);
    text-transform: uppercase;
    white-space: nowrap;
    flex-shrink: 0
}

.hpp-rec-select {
    height: 26px;
    padding: 0 6px;
    background: rgba(0,0,0,.6);
    border: 1px solid rgba(255,216,75,.35);
    border-radius: 3px;
    color: rgba(255,216,75,.85);
    font-size: 11px;
    cursor: pointer;
    flex-shrink: 0
}

.hpp-rec-select:focus {
    outline: 0;
    border-color: rgba(255,216,75,.65)
}

.hpp-rec-metro-btn {
    padding: .3rem .75rem;
    font-size: 10px;
    height: 26px;
    line-height: 1;
    flex-shrink: 0
}

.hpp-rec-metro-div {
    min-width: 52px
}

/* Reads as the same control as the Media Recorder's Play & Record Audio
   (.mr-console-rec.bws-button-a-v2): a flat red at rest, solid on hover, white
   label. Only the sizing is this button's own — it keeps its 28px row height and
   11px label rather than the console button's larger box. */
.hpp-rec-record-btn {
    /* One fixed width for every label it takes ("Record to Composition",
       "Stop Recording", the count-in) so the recorder row never reflows. */
    box-sizing: border-box;
    width: 180px;
    height: 28px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: 8px;
    padding: 0 1rem;
    border-radius: 4px;
    border: 1px solid #dc3545;
    background: rgba(220,53,69,.7);
    color: #fff;
    font-size: 11px;
    font-weight: 400;
    letter-spacing: .06em;
    cursor: pointer;
    white-space: nowrap;
    flex-shrink: 0;
    box-shadow: none;
    transition: background-color .2s ease
}

/* Record dot — the same "this take is being captured" mark the Media Recorder
   button carries (.mr-rec-mark). Drawn as a pseudo-element rather than an <svg>
   because the players set the button's label with textContent, which would wipe
   a real child element. */
.hpp-rec-record-btn::before {
    content: '';
    flex: 0 0 auto;
    width: 10px;
    height: 10px;
    border-radius: 50%;
    background: #fff
}

.hpp-rec-record-btn:hover {
    background: #dc3545;
    border-color: #dc3545;
    color: #fff;
    box-shadow: none
}

.hpp-rec-record-btn.recording {
    border-color: #dc3545;
    background: #dc3545;
    color: #fff;
    animation: hpp-rec-pulse .8s ease-in-out infinite alternate
}

.hpp-rec-countdown {
    inset: 0;
    z-index: 20;
    display: flex;
    align-items: center;
    justify-content: center;
    font-size: 180px;
    font-weight: 900;
    color: rgba(255,216,75,.9);
    text-shadow: 0 0 60px rgba(255,216,75,.6);
    background: rgba(0,0,0,.35);
    animation: hpp-countdown-pop .15s ease-out
}

.hpp-hint {
    bottom: 1rem;
    left: 50%;
    transform: translateX(-50%);
    z-index: 4;
    font-size: 11px;
    color: var(--text-muted);
    text-align: center;
    letter-spacing: .03em;
    white-space: nowrap
}

.hpp-feel-group,.hpp-rhythm-group,.hpp-sequencer-section {
    padding: 1rem;
    background: var(--bg-dark-heavy);
    border: 1px solid var(--border-accent-light);
    border-radius: 4px
}

.hpp-feel-group,.hpp-rhythm-group {
    flex: 1
}

.hpp-sequencer-section {
    position: relative;
    margin: 0 1rem 38px
}

/* .hpp-insert-row-btn / -dup-row-btn: see the shared row-action rules below. */

.hpp-seg-copy-btn,.hpp-seg-paste-btn {
    color: rgba(255,216,75,.7)
}

.hpp-seg-reset-btn {
    color: rgba(220,60,60,.75);
    border-color: rgba(220,60,60,.4);
    background: #120505
}

.hpp-seg-reset-btn:hover {
    color: #dc3c3c;
    background: #210909;
    border-color: rgba(220,60,60,.6)
}

.hpp-seg-copy-btn:hover,.hpp-seg-paste-btn.hpp-paste-ready:hover {
    color: var(--color-accent);
    background: #26200b;
    border-color: rgba(255,216,75,.5)
}

.hpp-step::after,.hpp-step::before {
    display: none
}

.hpp-step-bars {
    position: absolute;
    bottom: 0;
    left: 0;
    width: 100%;
    height: 100%;
    display: flex;
    align-items: flex-end;
    pointer-events: none
}

.hpp-step-bar {
    flex-shrink: 0;
    height: 0%;
    transition: height .08s ease,width .08s ease
}

.hpp-step-bar[data-bar-type=y] {
    background: rgba(255,216,75,.85)
}

.hpp-step-bar[data-bar-type=m] {
    background: rgba(170,90,240,.85)
}

.hpp-step-bar[data-bar-type=h] {
    background: rgba(70,160,255,.85)
}

.hpp-step-bar[data-bar-type=r] {
    background: rgba(220,60,60,.85)
}

.hpp-step-bar[data-bar-type=rim] {
    background: rgba(255,255,255,.7)
}

.hpp-step-bar[data-bar-type=gu] {
    background: rgba(60,200,100,.8)
}

.hpp-step.playing {
    border-color: #ffd84b;
    box-shadow: 0 0 0 2px rgba(255,216,75,.9),inset 0 0 6px rgba(255,216,75,.15)
}

.hpp-step:hover {
    border-color: rgba(255,216,75,.5)
}

.hpp-steps-16 .hpp-note-picker:hover {
    transform: scale(1.7);
    z-index: 10
}

.hpp-note-picker:hover .hpp-step-btn-bar {
    opacity: 1
}

/* The 16-step picker scales up on hover (.18s). The action bar counter-scales
   by that same factor at all times — never only while hovered — so it never
   animates against the parent: its declared size is its on-screen size.
   Visibility is a hard switch rather than a fade, held back until the zoom has
   finished, so the buttons are only ever shown while the picker sits still at
   full scale and vanish the instant the pointer leaves. */
.hpp-steps-16 .hpp-note-picker .hpp-step-btn-bar,
.sdp-steps-16 .sdp-sound-picker .sdp-step-btn-bar,
.sgp-steps-16 .sgp-sound-picker .sgp-step-btn-bar {
    transform: scale(.588);
    transform-origin: top center;
    transition: opacity 0s
}

.hpp-steps-16 .hpp-note-picker:hover .hpp-step-btn-bar,
.sdp-steps-16 .sdp-sound-picker:hover .sdp-step-btn-bar,
.sgp-steps-16 .sgp-sound-picker:hover .sgp-step-btn-bar {
    transition: opacity 0s .18s
}

/* The per-step delete/copy/paste trio takes the row trio's colours and hover
   response, but a smaller box — three of them sit under one step cell. */
.hpp-step-action-btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 28px;
    height: 28px;
    border-radius: 4px;
    border: 1px solid rgba(255,216,75,.45);
    background: #1a1608;
    color: rgba(255,216,75,.75);
    cursor: pointer;
    padding: 0;
    flex-shrink: 0;
    transition: color .15s,background .15s,border-color .15s,transform .1s
}

.hpp-step-action-btn:hover {
    color: var(--color-accent);
    background: #26200b;
    border-color: rgba(255,216,75,.55);
    transform: scale(1.1)
}

/* Red like the row reset, at rest and on hover — declared after the generic
   step-button rules so it wins. */
.hpp-step-reset-btn {
    color: rgba(220,60,60,.75);
    border-color: rgba(220,60,60,.4);
    background: #120505
}

.hpp-step-reset-btn:hover {
    color: #dc3c3c;
    background: #210909;
    border-color: rgba(220,60,60,.6);
    transform: scale(1.1)
}

.hpp-step-copy-active {
    color: var(--color-accent)!important;
    background: #383011!important;
    border-color: rgba(255,216,75,.75)!important
}

.hpp-note-picker-svg {
    width: 100%;
    height: 100%;
    display: block
}

/* Styled custom dropdowns (Type / Intonation / Tuning / Melody) — mirrors the timer modules */
.hpp-cd-trigger {
    display: flex; align-items: center; text-align: left; cursor: pointer;
    color: var(--text-muted)
}
.hpp-cd-trigger.has-value { color: var(--color-accent) }
.hpp-cd-trigger .hpp-cd-trigger-text,
.sbp-bowl-type-trigger-text {
    flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap
}
/* ── Shared preset-dropdown popup ────────────────────────────────────────────
   ONE skin for every rich dropdown in the app: the Singing Bowls / Handpan /
   Gong / Drum pickers (.hpp-preset-dropdown) and the Binaural, Frequency and
   Isochronic Timers preset + function popups (.bt-/.ft-/.it-preset-dropdown).
   The four class vocabularies are historical — the declarations below are the
   single source of truth, so edit a value once and every dropdown follows.
   Item rows optionally carry a 46px thumbnail (*-pd-img) and a second line
   (*-pd-title); the text-only dropdowns simply omit them. */
.hpp-preset-dropdown,
.bt-preset-dropdown,
.ft-preset-dropdown,
.it-preset-dropdown {
    position: fixed; z-index: 4000; display: flex; flex-direction: column;
    background: rgba(8,8,8,.98); border: 1px solid var(--color-accent); border-radius: 5px;
    box-shadow: 0 0 10px var(--glow-accent), 0 8px 24px rgba(0,0,0,.6); overflow: hidden;
    font-family: 'Roboto', -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif
}
.hpp-preset-dropdown .hpp-pd-bar,
.bt-preset-dropdown .bt-pd-bar,
.ft-preset-dropdown .ft-pd-bar,
.it-preset-dropdown .it-pd-bar {
    display: flex; align-items: center; justify-content: space-between; gap: .5rem;
    padding: .6rem .75rem; border-bottom: 1px solid var(--border-accent-light); flex-shrink: 0
}
/* The bar title reads as a header, not as a value: the same grey as the close
   button beside it, set in caps, so the accent colour stays on the options. */
.hpp-preset-dropdown .hpp-pd-bar-title,
.bt-preset-dropdown .bt-pd-bar-title,
.ft-preset-dropdown .ft-pd-bar-title,
.it-preset-dropdown .it-pd-bar-title {
    color: rgba(211,211,211,1); font-size: 11px; font-weight: 700; letter-spacing: .5px;
    text-transform: uppercase
}
.hpp-preset-dropdown .hpp-pd-close,
.bt-preset-dropdown .bt-pd-close,
.ft-preset-dropdown .ft-pd-close,
.it-preset-dropdown .it-pd-close {
    width: 22px; height: 22px; border-radius: 50%; border: 0; cursor: pointer; flex-shrink: 0;
    background: rgba(211,211,211,1); color: rgba(20,20,20,.95); font-size: 11px; line-height: 1;
    display: flex; align-items: center; justify-content: center
}
.hpp-preset-dropdown .hpp-pd-close:hover,
.bt-preset-dropdown .bt-pd-close:hover,
.ft-preset-dropdown .ft-pd-close:hover,
.it-preset-dropdown .it-pd-close:hover {
    background: var(--color-danger); color: #fff
}
.hpp-preset-dropdown .hpp-pd-list,
.bt-preset-dropdown .bt-pd-list,
.ft-preset-dropdown .ft-pd-list,
.it-preset-dropdown .it-pd-list {
    overflow-y: auto; -webkit-overflow-scrolling: touch; overscroll-behavior: contain;
    scrollbar-width: thin; scrollbar-color: var(--color-accent) #000
}
.hpp-preset-dropdown .hpp-pd-list::-webkit-scrollbar,
.bt-preset-dropdown .bt-pd-list::-webkit-scrollbar,
.ft-preset-dropdown .ft-pd-list::-webkit-scrollbar,
.it-preset-dropdown .it-pd-list::-webkit-scrollbar {
    width: 10px
}
.hpp-preset-dropdown .hpp-pd-list::-webkit-scrollbar-track,
.bt-preset-dropdown .bt-pd-list::-webkit-scrollbar-track,
.ft-preset-dropdown .ft-pd-list::-webkit-scrollbar-track,
.it-preset-dropdown .it-pd-list::-webkit-scrollbar-track {
    background: #000
}
.hpp-preset-dropdown .hpp-pd-list::-webkit-scrollbar-thumb,
.bt-preset-dropdown .bt-pd-list::-webkit-scrollbar-thumb,
.ft-preset-dropdown .ft-pd-list::-webkit-scrollbar-thumb,
.it-preset-dropdown .it-pd-list::-webkit-scrollbar-thumb {
    background: var(--color-accent); border-radius: 6px;
    border: 2px solid #000; background-clip: padding-box
}
.hpp-preset-dropdown .hpp-pd-header,
.bt-preset-dropdown .bt-pd-header,
.ft-preset-dropdown .ft-pd-header,
.it-preset-dropdown .it-pd-header {
    display: flex; align-items: center; justify-content: space-between; gap: .5rem;
    padding: .7rem .75rem; background: rgba(255,216,75,.08);
    color: rgba(255,255,255,0.9); font-size: 13px; font-weight: 500; line-height: 1.2; letter-spacing: .3px;
    border-bottom: 1px solid var(--border-accent-light); cursor: pointer; user-select: none
}
.hpp-preset-dropdown .hpp-pd-header-label,
.bt-preset-dropdown .bt-pd-header-label,
.ft-preset-dropdown .ft-pd-header-label,
.it-preset-dropdown .it-pd-header-label {
    min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap
}
.hpp-preset-dropdown .hpp-pd-chevron,
.bt-preset-dropdown .bt-pd-chevron,
.ft-preset-dropdown .ft-pd-chevron,
.it-preset-dropdown .it-pd-chevron {
    flex-shrink: 0; transition: transform .18s ease
}
.hpp-preset-dropdown .hpp-pd-header.collapsed .hpp-pd-chevron,
.bt-preset-dropdown .bt-pd-header.collapsed .bt-pd-chevron,
.ft-preset-dropdown .ft-pd-header.collapsed .ft-pd-chevron,
.it-preset-dropdown .it-pd-header.collapsed .it-pd-chevron {
    transform: rotate(-90deg)
}
.hpp-preset-dropdown .hpp-pd-group.collapsed,
.bt-preset-dropdown .bt-pd-group.collapsed,
.ft-preset-dropdown .ft-pd-group.collapsed,
.it-preset-dropdown .it-pd-group.collapsed {
    display: none
}
.hpp-preset-dropdown .hpp-pd-row,
.bt-preset-dropdown .bt-pd-row,
.ft-preset-dropdown .ft-pd-row,
.it-preset-dropdown .it-pd-row {
    display: flex; align-items: center; border-bottom: 1px solid rgba(255,216,75,.07)
}
.hpp-preset-dropdown .hpp-pd-row:hover,
.bt-preset-dropdown .bt-pd-row:hover,
.ft-preset-dropdown .ft-pd-row:hover,
.it-preset-dropdown .it-pd-row:hover {
    background: rgba(255,216,75,.1)
}
.hpp-preset-dropdown .hpp-pd-row.selected,
.bt-preset-dropdown .bt-pd-row.selected,
.ft-preset-dropdown .ft-pd-row.selected,
.it-preset-dropdown .it-pd-row.selected {
    background: rgba(255,216,75,.16)
}
.hpp-preset-dropdown .hpp-pd-item,
.bt-preset-dropdown .bt-pd-item,
.ft-preset-dropdown .ft-pd-item,
.it-preset-dropdown .it-pd-item {
    display: flex; align-items: center; gap: .6rem; flex: 1; min-width: 0;
    padding: .6rem .5rem .6rem .75rem; background: none; border: 0; cursor: pointer; text-align: left
}
.hpp-preset-dropdown .hpp-pd-info,
.bt-preset-dropdown .bt-pd-info,
.ft-preset-dropdown .ft-pd-info,
.it-preset-dropdown .it-pd-info {
    margin-right: .6rem
}
.hpp-preset-dropdown .hpp-pd-img,
.bt-preset-dropdown .bt-pd-img,
.ft-preset-dropdown .ft-pd-img,
.it-preset-dropdown .it-pd-img {
    width: 46px; height: 46px; border-radius: 4px; object-fit: cover; flex-shrink: 0;
    background: rgba(255,216,75,.08)
}
.hpp-preset-dropdown .hpp-pd-img-ph,
.bt-preset-dropdown .bt-pd-img-ph,
.ft-preset-dropdown .ft-pd-img-ph,
.it-preset-dropdown .it-pd-img-ph {
    border: 1px solid var(--border-accent-light)
}
.hpp-preset-dropdown .hpp-pd-img-oster,
.bt-preset-dropdown .bt-pd-img-oster,
.ft-preset-dropdown .ft-pd-img-oster,
.it-preset-dropdown .it-pd-img-oster {
    display: flex; align-items: center; justify-content: center;
    color: var(--color-accent); font-size: 15px; font-weight: 700; letter-spacing: .5px;
    background: rgba(255,216,75,.12); border-color: var(--color-accent)
}
.hpp-preset-dropdown .hpp-pd-text,
.bt-preset-dropdown .bt-pd-text,
.ft-preset-dropdown .ft-pd-text,
.it-preset-dropdown .it-pd-text {
    display: flex; flex-direction: column; gap: 2px; min-width: 0
}
.hpp-preset-dropdown .hpp-pd-value,
.bt-preset-dropdown .bt-pd-value,
.ft-preset-dropdown .ft-pd-value,
.it-preset-dropdown .it-pd-value {
    color: var(--color-accent); font-size: 13px; font-weight: 500; line-height: 1.2;
    white-space: nowrap; overflow: hidden; text-overflow: ellipsis
}
.hpp-preset-dropdown .hpp-pd-title,
.bt-preset-dropdown .bt-pd-title,
.ft-preset-dropdown .ft-pd-title,
.it-preset-dropdown .it-pd-title {
    color: rgba(255,255,255,.85); font-size: 12px; font-weight: 500; line-height: 1.25
}
.hpp-preset-dropdown .hpp-pd-empty,
.bt-preset-dropdown .bt-pd-empty,
.ft-preset-dropdown .ft-pd-empty,
.it-preset-dropdown .it-pd-empty {
    padding: 1rem; color: var(--text-muted); font-size: 13px; text-align: center
}

/* Compact styled-dropdown variant — shared trigger + popup for small inline selects
   (e.g. Hit Power, recorder Tempo / Time / Row). Reuse by giving the trigger class
   .bws-cd-sm and the popup the .bws-pd-sm modifier. */
.bws-cd-sm {
    display: inline-flex; align-items: center; text-align: left;
    height: 26px; padding: 0 22px 0 8px; box-sizing: border-box;
    background: rgba(0,0,0,.6) url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23ffd84b' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M6 9l6 6 6-6'%3E%3C/path%3E%3C/svg%3E") no-repeat right 6px center;
    background-size: .55rem;
    border: 1px solid rgba(255,216,75,.35); border-radius: 3px;
    color: rgba(255,216,75,.85); font-size: 11px; cursor: pointer; flex-shrink: 0;
}
.bws-cd-sm:hover { border-color: rgba(255,216,75,.6) }
.bws-cd-sm .bws-cd-sm-text { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap }

/* Session Recorder fields: fixed to the widest value each one can show ("180",
   "4/4", "16 (step)"), so picking a shorter value does not shrink the field and
   shuffle the row. The width goes on both the <select> and the styled trigger
   the players inject next to it. */
.hpp-rec-tempo-select, .hpp-rec-tempo-select + .bws-cd-sm {
    box-sizing: border-box; width: 52px; min-width: 52px; max-width: 52px
}
.hpp-rec-time-select, .hpp-rec-time-select + .bws-cd-sm {
    box-sizing: border-box; width: 50px; min-width: 50px; max-width: 50px
}
.hpp-rec-metro-div, .hpp-rec-metro-div + .bws-cd-sm {
    box-sizing: border-box; width: 78px; min-width: 78px; max-width: 78px
}
.hpp-preset-dropdown.bws-pd-sm .hpp-pd-bar-title { font-size: 11px }
.hpp-preset-dropdown.bws-pd-sm .hpp-pd-value { font-size: 11px }
.hpp-preset-dropdown.bws-pd-sm .hpp-pd-item { padding: .35rem .5rem .35rem .6rem }

/* Red variant of the compact dropdown — the recorder's Start Row picker, which
   belongs to the red Record button next to it rather than to the accent controls
   in the rest of the row. The <select> carries data-pd-variant="bws-pd-rec", which
   the players copy onto the popup, so the open list matches the closed field. */
.hpp-rec-row-select + .bws-cd-sm {
    background-color: rgba(0,0,0,.6);
    background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23ffffff' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M6 9l6 6 6-6'%3E%3C/path%3E%3C/svg%3E");
    border-color: #dc3545;
    color: #fff
}
.hpp-rec-row-select + .bws-cd-sm:hover { border-color: #dc3545; background-color: rgba(220,53,69,.25) }

.hpp-preset-dropdown.bws-pd-rec {
    border-color: #dc3545;
    box-shadow: 0 0 10px rgba(220,53,69,.35), 0 8px 24px rgba(0,0,0,.6)
}
.hpp-preset-dropdown.bws-pd-rec .hpp-pd-bar { border-bottom-color: rgba(220,53,69,.45) }
.hpp-preset-dropdown.bws-pd-rec .hpp-pd-value { color: #fff }
.hpp-preset-dropdown.bws-pd-rec .hpp-pd-row { border-bottom-color: rgba(220,53,69,.18) }
.hpp-preset-dropdown.bws-pd-rec .hpp-pd-row:hover { background: rgba(220,53,69,.22) }
.hpp-preset-dropdown.bws-pd-rec .hpp-pd-row.selected { background: rgba(220,53,69,.35) }
.hpp-preset-dropdown.bws-pd-rec .hpp-pd-list { scrollbar-color: #dc3545 #000 }
.hpp-preset-dropdown.bws-pd-rec .hpp-pd-list::-webkit-scrollbar-thumb { background: #dc3545 }

/* Singing Bowls — 2-section session controls card */
.sbp-session-card {
    background: var(--bg-dark-light);
    border: 1px solid var(--border-accent-light);
    border-radius: 4px;
    margin-bottom: .75rem;
}
.sbp-session-card .bws-button-a.playing {
    border: 1px solid #dc3545!important;
    background: var(--bg-danger)!important;
    color: #fff!important;
    box-shadow: inset 0 0 10px var(--glow-danger)!important;
}
.sbp-session-card .bws-button-a.playing:hover { background: rgba(220,53,69,.7)!important }
.sbp-controls-top {
    display: flex; align-items: center; justify-content: space-between;
    gap: 1.5rem; padding: .75rem;
    border-bottom: 1px solid var(--border-accent-light);
}
.sbp-controls-bottom {
    display: flex; align-items: center; gap: 1rem; padding: .6rem .75rem;
}
.sbp-controls-bottom .bws-volume-control { flex: 1 }
.sbp-controls-bottom .hpp-player-row { flex: 1; min-width: 0; margin-top: 0 }
/* Same field-label recipe as the top bar and the bowl controls
   (.hpp-melody-label / .bowl-frequency-title / .bws-fbar-field label). */
.sbp-ctrl-label {
    font-size: 11px; color: var(--text-muted, #aaa); font-weight: 700;
    text-transform: uppercase; letter-spacing: .08em; white-space: nowrap; flex: 0 0 auto;
}
.sbp-duration-group, .sbp-spatial-group { display: flex; align-items: center; gap: .6rem }
.sbp-spatial-toggles { display: flex; align-items: center; gap: .5rem }
.sbp-spatial-toggles .bws-toggle-btn { padding: .4rem 1.5rem; min-width: 160px }

.hpp-melody-row {
    display: flex;
    align-items: center;
    gap: 12px;
    margin: 0 1rem .5rem;
    padding: .55rem 1rem;
    background: var(--bg-dark-heavy);
    border: 1px solid var(--border-accent-light);
    border-radius: 4px
}

/* ── Sticky control bars ────────────────────────────────────────────────────
   .hpp-melody-row (Time / Step Edit / Hover / Power Control / Hit Power) and
   .hpp-player-row (transport + track volume) both drive the body between them, so
   both stay reachable for as long as that body is on screen: the controls pin under
   the fixed app header, the transport to the bottom of the screen, and both scroll
   away with .bws-sticky-wrap, the element that holds them and the body.
   The handpan, gong and drum composition builders use both bars around their
   sequencer rows; the timer, playlist, singing bowls and Media Mixer modules use the
   bottom bar alone, around their timer list / playlist / session bands / channels.
   Either bar may be absent, and the bottom one comes in four flavours:
   .hpp-player-row, .pl-transport-row, .sbp-session-card, .tin-session-card and
   .mr-player-panel.

   Sticky resolves against the nearest scrollport, and .bbp-module-toggle-animated
   gives the open module `overflow: hidden` — which is a scrollport, and would pin
   the row inside the module instead of to the viewport. Once the module has
   finished opening its max-height cap is off and nothing overflows vertically any
   more, and the bottom bar deliberately overhangs the box on the left, so the open
   module stops clipping altogether. `visible` is not a scroll container either, so
   sticky still resolves against the viewport.
   Matched through :has() so a module only has to add the wrapper to opt in. */
.bws-module-content.bbp-module-open:has(> .bws-sticky-wrap) {
    overflow: visible
}

.bws-sticky-wrap {
    display: flex;
    flex-direction: column;
    /* The two used to be siblings in .bws-module-content; inheriting its gap keeps
       the spacing between controls and rows unchanged at every breakpoint. */
    gap: inherit
}

/* Both bars: full-bleed to the .bws-module-content box's outer edges and an opaque
   plate of their own, since the sequencer rows travel underneath them. The row's own
   side margin is replaced by that box's padding + border pulled back as negative
   margin; the insets are measured in JS because the box re-pads at every breakpoint. */
.bws-sticky-wrap > .hpp-melody-row,
.bws-sticky-wrap > .hpp-player-row,
.bws-sticky-wrap > .pl-transport-row,
.bws-sticky-wrap > .sbp-session-card,
.bws-sticky-wrap > .tin-session-card,
.bws-sticky-wrap > .mr-player-panel,
.bws-sticky-wrap > .mex-console-box {
    position: sticky;
    z-index: 20;  /* above a hovered picker (10), below the app header (1100) */
    margin-left: calc(-1 * var(--bws-bar-inset-l, 1rem));
    margin-right: calc(-1 * var(--bws-bar-inset-r, 1rem));
    border: 1px solid var(--border-accent-light);
    border-left: 0;
    border-right: 0;
    border-radius: 0;
    background: var(--bws-sticky-bg);
    backdrop-filter: blur(8px);
    -webkit-backdrop-filter: blur(8px)
}

.bws-sticky-wrap > .hpp-melody-row {
    /* No app header on the module's own standalone page — see the body override. */
    top: var(--bws-sticky-top, var(--bws-header-h, 52px));
    box-shadow: var(--bws-sticky-shadow, 0 6px 16px rgba(0,0,0,.45))
}

/* The transport pins to the bottom of the screen instead, so its controls stay
   reachable from anywhere in the rows. Unlike the controls row it has no chrome of
   its own, so it borrows the melody row's padding; the shadow is cast upward.
   The negative bottom margin drops it onto the module box's own bottom edge — no
   strip of module background left under the bar once it comes to rest — which is
   also why it has no bottom border: the module's closes it off. */
.bws-sticky-wrap > .hpp-player-row,
.bws-sticky-wrap > .pl-transport-row,
.bws-sticky-wrap > .sbp-session-card,
.bws-sticky-wrap > .tin-session-card,
.bws-sticky-wrap > .mr-player-panel,
.bws-sticky-wrap > .mex-console-box {
    bottom: 0;
    padding: .55rem 1rem;
    margin-bottom: calc(-1 * var(--bws-bar-inset-b, 1rem));
    border-bottom: 0;
    box-shadow: var(--bws-sticky-shadow-up, 0 -6px 16px rgba(0,0,0,.45))
}

/* Every sticky bar — the controls row on top as well as the transport below —
   reaches a little further left than its module box, so the module's own icon can sit
   out there and say whose controls these are while the bar is pinned over someone
   else's module. Both bars of a wrapper share one jut, so their icons line up. JS answers --bws-bar-jut-want with however
   much of it fits (0 where none does — then the brand column simply sits inside the
   bar and the controls start after it, nothing is cut off). */
.bws-sticky-wrap > .hpp-melody-row,
.bws-sticky-wrap > .hpp-player-row,
.bws-sticky-wrap > .pl-transport-row,
.bws-sticky-wrap > .sbp-session-card,
.bws-sticky-wrap > .tin-session-card,
.bws-sticky-wrap > .mr-player-panel,
.bws-sticky-wrap > .mex-console-box {
    --bws-bar-jut-want: 68px;
    /* The brand column runs from the bar's left edge to the separator: the overhang
       plus the bar's own left padding, floored at what the icon needs on its own. */
    --bws-bar-brand-w: max(52px, calc(var(--bws-bar-jut, 0px) + 1rem));
    margin-left: calc(-1 * (var(--bws-bar-inset-l, 1rem) + var(--bws-bar-jut, 0px)));
    padding-left: calc(var(--bws-bar-brand-w) + 1.1rem)
}

/* Absolute rather than a flex child, so a bar lays its controls out exactly as it
   did before — the brand column is carved out of the padding instead. */
.bws-bar-brand {
    position: absolute;
    left: 0;
    top: 0;
    bottom: 0;
    width: var(--bws-bar-brand-w);
    display: flex;
    align-items: center;
    justify-content: center;
    /* A hairline between the icon and the controls it labels: the icon is an
       identifier, not one of them. */
    border-right: 1px solid var(--color-border, rgba(255,255,255,.18));
    pointer-events: none
}

/* Same grey as the transport's time readouts (.hpp-player-time) — the icon reads as
   a label on the bar rather than a lit control. */
.bws-bar-brand > svg {
    width: 22px;
    height: 22px;
    fill: var(--text-muted, rgba(255,255,255,0.55))
}

/* Inside the bar, the transport's own bordered box would be a panel within a panel,
   so its controls sit straight on the bar instead. Scoped to the wrapper: .hpp-player
   is shared with the media library and the Media Mixer's master bar, and the playlist
   transports ([id$="-pl-transport"]) keep their box everywhere else too. */
.bws-sticky-wrap > .hpp-player-row > .hpp-player,
.bws-sticky-wrap > .pl-transport-row > [id$="-pl-transport"],
.bws-sticky-wrap > .mr-player-panel .hpp-player {
    /* margin too: the breathwork and tinnitus bars carried a .25rem top margin from
       when they sat in the flow of their step. */
    margin: 0;
    padding: 0;
    background: none;
    border: 0;
    border-radius: 0
}

/* The section navigator scrolls a section to the top of the scrollport; html's
   scroll-padding-top already clears the app header, and this clears the pinned
   controls sitting under it. --bws-sticky-h is measured in JS. */
/* The singing bowls card is two padded rows of its own — duration + spatial above,
   transport below — so it brings its own padding rather than the bar's, except on
   the left where the brand column has to fit. */
.bws-sticky-wrap > .sbp-session-card,
.bws-sticky-wrap > .tin-session-card,
.bws-sticky-wrap > .mex-console-box {
    padding-top: 0;
    padding-right: 0;
    padding-bottom: 0
}

.bws-sticky-wrap > [id$="-rows-container"] > * {
    scroll-margin-top: calc(var(--bws-sticky-h, 46px) + 8px)
}

/* The modules' own standalone pages carry no app header, so there is nothing to
   clear there. */
body#brainwavesounds-handpan-module,
body#brainwavesounds-sacred-gong-module,
body#brainwavesounds-shamanic-drum-module,
body#brainwavesounds-singing-bowls-module,
body#brainwavesounds-audio-visualizer-pro2-module {
    --bws-sticky-top: 0px
}

/* Singing Bowls: the Bowl Type / Frequency Preset / Synchronization bar. Same plate
   as the gong's pattern row (it IS .hpp-melody-row); this only lays the controls out,
   sharing the width evenly and wrapping to a second line rather than squeezing on a
   narrow screen. The label text of each control carries its old description as a title. */
.sbp-top-bar {
    flex-wrap: wrap;
    row-gap: 8px;
    /* Flush with the top of the module box, the way it is already flush with its left
       and right edges: the box's top padding and border pulled back, so the bar's top
       border lands on the module's own rather than sitting 17px inside it. Measured in
       JS with the other three insets, because the box re-pads at every breakpoint. */
    margin-top: calc(-1 * var(--bws-bar-inset-t, 1rem))
}

.sbp-top-bar > .bws-dropdown,
.sbp-top-bar > .sbp-bowl-type-trigger {
    flex: 1 1 180px;
    min-width: 0
}

/* The three controls do not want the same width: preset names are long enough to
   truncate at an even split, while the sync sources are short. Give the spare
   width to Frequencies and take it off Synchronization. */
.sbp-top-bar > [data-for="sbp-upper-frequency-select"] {
    flex: 2 1 240px
}

.sbp-top-bar > [data-for="sbp-upper-sync-select"] {
    flex: .7 1 140px
}

.sbp-top-bar > .sbp-config-input {
    flex: 1 1 100%;
    min-width: 0
}

/* Field label for every sticky top bar — Singing Bowls' Bowl Type / Frequencies /
   Synchronization, the Handpan's Time, the Drum's Rhythm + Time and the Gong's
   Pattern + Time. Muted grey, like the other field labels across the app
   (.sbp-ctrl-label, .bowl-frequency-title, .bws-fbar-field label). */
.hpp-melody-label {
    font-size: 11px;
    font-weight: 700;
    color: var(--text-muted, #aaa);
    text-transform: uppercase;
    letter-spacing: .08em;
    white-space: nowrap;
    flex-shrink: 0
}

.hpp-melody-action-btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 28px;
    height: 28px;
    border-radius: 4px;
    border: 1px solid rgba(255,216,75,.45);
    background: rgba(255,216,75,.1);
    color: rgba(255,216,75,.75);
    cursor: pointer;
    flex-shrink: 0;
    padding: 0;
    transition: color .15s,background .15s,border-color .15s,transform .1s
}

.hpp-melody-action-btn:hover {
    color: var(--color-accent);
    background: rgba(255,216,75,.18);
    border-color: rgba(255,216,75,.7);
    transform: scale(1.1)
}

.hpp-keyboard-toggle,.sdp-keyboard-toggle,.sgp-keyboard-toggle {
    position: absolute;
    top: 10px;
    left: 10px;
    min-width: 170px;
    z-index: 10;
    padding: .5rem 1.25rem
}

.player-power-controls {
    position: absolute;
    top: 10px;
    right: 10px;
    z-index: 10;
    display: flex;
    align-items: center;
    gap: 8px
}

.player-power-toggle {
    min-width: 170px;
    padding: .5rem 1.25rem
}

.hpp-fixed-seq-btn {
    min-width: 150px
}

.hpp-seq-row-header {
    display: flex;
    justify-content: space-between;
    align-items: center;
    margin-bottom: .75rem;
    gap: 10px;
    flex-wrap: wrap
}

.hpp-row-copy-btn,.hpp-row-paste-btn {
    color: rgba(255,216,75,.75)
}

.hpp-row-copy-btn:hover,.hpp-row-paste-btn.hpp-paste-ready:hover {
    color: var(--color-accent);
    background: #26200b;
    border-color: rgba(255,216,75,.55);
    transform: scale(1.1)
}

.hpp-row-paste-btn.hpp-paste-ready {
    opacity: 1;
    pointer-events: auto;
    color: rgba(255,216,75,.6)
}

/* Tempo override (handpan / gong / drum): dim the placeholder (shows live main
   tempo when inheriting), accent the field border when a per-row BPM is set. */
.hpp-tempo-override-input::placeholder,
.sgp-tempo-override-input::placeholder,
.sdp-tempo-override-input::placeholder { color: var(--text-muted, rgba(255,255,255,0.4)); }
.hpp-tempo-override-input.hpp-override-active,
.sgp-tempo-override-input.sgp-override-active,
.sdp-tempo-override-input.sdp-override-active {
    border-color: var(--color-accent, #ffd84b);
    color: var(--color-text, #fff);
    box-shadow: inset 0 0 6px rgba(255,216,75,0.25);
}

@media (max-width:600px) {
    .hpp-preset-row {
        flex-direction: column
    }

    .bws-sequencer-grid {
        gap: 4px
    }

    .hpp-seq-beat-pickers,.hpp-seq-beat-steps {
        gap: 2px
    }

    .hpp-step {
        height: 40px
    }
}
/* ── Media Recorder horizontal channel strips ──────────────────────────── */

.mr-module-col {
    display: flex;
    flex-direction: row;
    align-items: center;
    gap: 8px
}

/* Toggle icon sits on the left at a fixed compact width */
.mr-module-col .mr-module-toggle-button {
    flex: 0 0 auto;
    width: 84px
}

.mr-vol-slider {
    order: 1;
    writing-mode: horizontal-tb!important;
    direction: ltr!important;
    -webkit-appearance: none!important;
    appearance: none!important;
    height: 6px!important;
    width: 300px!important;
    max-width: 100%;
    flex: 0 1 auto;
    min-width: 80px;
    min-height: 0;
    border-radius: 3px!important;
    border: 0!important;
    outline: 0!important;
    box-shadow: none!important;
    cursor: pointer;
    background: linear-gradient(to right,var(--color-accent) 0%,var(--color-accent) var(--val,75%),rgba(255,255,255,.15) var(--val,75%))!important
}

.mr-vol-slider::-webkit-slider-thumb {
    -webkit-appearance: none!important;
    appearance: none!important;
    width: 14px!important;
    height: 14px!important;
    border-radius: 50%!important;
    background: var(--color-accent)!important;
    border: 0!important;
    cursor: pointer!important;
    box-shadow: 0 2px 4px rgba(0,0,0,.3)!important
}

.mr-vol-slider::-webkit-slider-thumb:hover {
    box-shadow: 0 4px 8px rgba(0,0,0,.5)!important
}

.mr-vol-slider::-webkit-slider-runnable-track {
    background: 0 0!important;
    border: 0!important
}

.mr-vol-slider::-moz-range-thumb {
    width: 14px!important;
    height: 14px!important;
    border-radius: 50%!important;
    background: var(--color-accent)!important;
    border: 0!important;
    cursor: pointer!important;
    box-shadow: 0 2px 4px rgba(0,0,0,.3)!important
}

.mr-vol-slider::-moz-range-thumb:hover {
    box-shadow: 0 4px 8px rgba(0,0,0,.5)!important
}

.mr-vol-slider::-moz-range-track {
    background: 0 0!important;
    border: 0!important
}

.mr-vol-slider:focus {
    outline: 0!important;
    box-shadow: none!important;
    border: 0!important
}

/* Tick marks removed — slider shows a clean fill only */

/* ── Singing Bowls XY Spatial Pad ─────────────────────────────────────── */

.sbp-xy-wrapper {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 6px
}

.sbp-xy-pad {
    width: 110px;
    height: 110px;
    background: rgba(255,255,255,.06);
    border: 1px solid rgba(255,255,255,.18);
    border-radius: 4px;
    position: relative;
    cursor: crosshair;
    touch-action: none;
    flex-shrink: 0
}

.sbp-xy-dot {
    width: 14px;
    height: 14px;
    background: var(--color-accent);
    border-radius: 50%;
    position: absolute;
    transform: translate(-50%, -50%);
    left: 50%;
    top: 0%;
    pointer-events: none;
    box-shadow: 0 0 6px var(--glow-accent)
}

/* Listener orientation cue inside the pad — see ui-manager.js. Sits on the bottom
   edge, a shade lighter than the pad itself so it reads as a marking on the surface
   rather than a control: the dot, the grid lines and this are three distinct weights. */
.sbp-xy-head {
    position: absolute;
    left: 50%;
    bottom: 2px;
    width: 38px;
    height: auto;
    transform: translateX(-50%);
    fill: rgba(255,255,255,.14);
    pointer-events: none
}

.sbp-xy-buttons {
    display: flex;
    gap: 4px;
    flex-wrap: wrap;
    justify-content: center
}

.sbp-xy-wrapper .bws-label.sbp-xy-label {
    font-size: 10px;
    font-weight: 700;
    letter-spacing: .06em;
    text-transform: uppercase;
    color: var(--text-muted, #aaa)
}

.sbp-xy-pad::before,
.sbp-xy-pad::after {
    content: '';
    position: absolute;
    background: rgba(255,255,255,.12);
    pointer-events: none
}

.sbp-xy-pad::before {
    left: 50%;
    top: 0;
    bottom: 0;
    width: 1px;
    transform: translateX(-50%)
}

.sbp-xy-pad::after {
    top: 50%;
    left: 0;
    right: 0;
    height: 1px;
    transform: translateY(-50%)
}

/* ── Timer card name label (shared across FT / BT / IT) ── */
.ft-timer-name, .bt-timer-name, .it-timer-name {
    font-family: 'Roboto',-apple-system,BlinkMacSystemFont,"Segoe UI","Helvetica Neue",sans-serif;
    color: var(--color-accent);
    font-size: 14px;
    font-weight: 400;
    text-transform: uppercase;
    letter-spacing: 0.5px;
    white-space: nowrap;
}

/* ── Timer module section header (label + sync dropdown row) ── */
.ft-section-header, .bt-section-header, .it-section-header {
    display: flex;
    align-items: center;
    justify-content: flex-end;
    gap: 0.6rem;
    margin-bottom: 0.5rem;
}
/* The sync row sits right on top of the timers it fills, so it pulls back most
   of the 1rem column gap .bws-module-content puts between its children. */
.bws-module-content > .bt-section-header,
.bws-module-content > .ft-section-header,
.bws-module-content > .it-section-header { margin-top: 0.5rem; margin-bottom: -0.85rem; }

/* Sync dropdown trigger — cap width so it doesn't stretch full-width */
.ft-sync-dropdown + .hpp-cd-trigger,
.bt-sync-dropdown + .hpp-cd-trigger,
.it-sync-dropdown + .hpp-cd-trigger { max-width: 300px; }

/* Handpan track player row: control bar + Play button to its right */
.hpp-player-row {
    display: flex;
    align-items: center;
    gap: 0.6rem;
    margin-top: 0.6rem;
}

/* Square Solo / Mute toggle stack — shared by the Media Mixer channels and the
   Handpan Composition Builder. Mirrors the shamanic-drum segment buttons. */
.mr-sm-stack {
    display: flex;
    flex-direction: column;
    gap: 4px;
    flex: 0 0 auto;
}
.mr-sm-btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 20px;
    height: 20px;
    border-radius: 3px;
    border: 1px solid rgba(255, 216, 75, 0.4);
    background: #1a1608;
    color: rgba(255, 216, 75, 0.7);
    font-family: inherit;
    font-size: 12px;
    font-weight: 500;
    line-height: 1;
    cursor: pointer;
    padding: 0;
    flex-shrink: 0;
    transition: color 0.15s, background 0.15s, border-color 0.15s;
}
.mr-sm-btn:hover {
    color: var(--color-accent, #ffd84b);
    background: #26200b;
    border-color: rgba(255, 216, 75, 0.5);
}
/* Solo active — bright accent fill */
.mr-solo-btn.is-active {
    color: #0c0c0c;
    background: var(--color-accent, #ffd84b);
    border-color: var(--color-accent, #ffd84b);
}
/* Mute active — red fill */
.mr-mute-btn.is-active {
    color: #000;
    background: #b3261e;
    border-color: rgba(220, 60, 60, 0.7);
}
.mr-mute-btn.is-active:hover {
    background: #dc3c3c;
    border-color: #dc3c3c;
}

/* Playlist / library control row: the bordered transport box on the left, with the
   S/M stack and green Play button as siblings OUTSIDE it (mirrors .hpp-player-row). */
.pl-transport-row { display: flex; align-items: center; gap: 0.55rem; margin-top: 0.85rem; }
.pl-transport-row > [id$="-pl-transport"] { flex: 1; min-width: 0; margin-top: 0; }
.pl-main-play { margin-top: 0; flex: 0 0 auto; }
/* Handpan track player control bar (position + volume) — mirrors media-library row player */
.hpp-player,
.mlib-player {
    display: flex;
    align-items: center;
    gap: 0.55rem;
    flex: 1;
    min-width: 0;
    padding: 0.65rem 0.75rem;
    background: var(--bg-dark-heavy, rgba(0,0,0,0.3));
    border: 1px solid var(--border-accent-medium);
    border-radius: 4px;
}
.hpp-player-btn,
.alb-player-btn,
.mlib-player-btn,
.ft-player-btn,
.bt-player-btn,
.it-player-btn {
    flex-shrink: 0;
    display: flex;
    align-items: center;
    justify-content: center;
    width: 26px;
    height: 26px;
    padding: 0;
    background: none;
    border: none;
    border-radius: 50%;
    color: var(--color-accent, #ffd84b);
    cursor: pointer;
    transition: color 0.15s, background 0.15s;
}
.hpp-play-btn:hover { background: rgba(255, 216, 75, 0.14); }
/* The speaker glyph in front of a volume slider. It LABELS the slider, it is not a
   control: clicking it used to mute, and that is gone everywhere (the click handlers
   were removed at each site). pointer-events: none is what makes it read that way —
   no pointer cursor, no hover colour, no tooltip, nothing to click.
   This is not the mixer's own S/M buttons (.mr-sm-btn), which still mute a channel. */
.hpp-mute-btn,
.alb-mute-btn,
.mlib-mute-btn {
    color: var(--text-muted, rgba(255,255,255,0.5));
    width: 22px;
    height: 22px;
    pointer-events: none;
    cursor: default;
}
.hpp-player-time,
.alb-player-time,
.mlib-player-time,
.ft-player-time,
.bt-player-time,
.it-player-time {
    flex-shrink: 0;
    font-size: 11px;
    font-variant-numeric: tabular-nums;
    color: var(--text-muted, rgba(255,255,255,0.55));
    min-width: 30px;
    text-align: center;
}
/* Position slider takes 3/4, volume slider 1/4 of the shared flex space.
   Both reuse .bws-volume-slider for the accent fill + round thumb.
   width:auto overrides the shared .bws-volume-slider width:100%. */
.hpp-player .hpp-seek,
.alb-seek,
.mlib-seek {
    flex: 3;
    min-width: 0;
    width: auto;
}
.hpp-player .hpp-vol,
.alb-vol,
.mlib-vol {
    flex: 1;
    min-width: 0;
    width: auto;
}
/* ── Composition position track (handpan / gong / shamanic drum): one rounded bar per section ─────────
   Same segmented bar the timer + playlist modules use (BWSSegBar,
   js/bws-seg-bar.js): a section's bar spans ALL of its repeats, so the breaks
   in the track are the points the prev/next-section buttons jump to. The
   slider keeps its own transparent track and rides above the bars on z-index,
   so the thumb is still the thing you grab. */
#hpp-main-parent-container .hpp-seek-wrap,
#sgp-main-parent-container .hpp-seek-wrap,
#sdp-main-parent-container .hpp-seek-wrap { position: relative; flex: 3; min-width: 0; display: flex; align-items: center; }
#hpp-main-parent-container .hpp-seek-wrap .hpp-seek,
#sgp-main-parent-container .hpp-seek-wrap .hpp-seek,
#sdp-main-parent-container .hpp-seek-wrap .hpp-seek { flex: 1; width: auto; position: relative; z-index: 1; background: transparent !important; }
#hpp-main-parent-container .hpp-seek-segs,
#sgp-main-parent-container .hpp-seek-segs,
#sdp-main-parent-container .hpp-seek-segs { position: absolute; left: 0; right: 0; top: 50%; height: 6px; transform: translateY(-50%); display: flex; align-items: center; gap: 1px; pointer-events: none; }
/* A looping row or section has no finite length, so the whole track (bars
   included) greys out with the slider rather than leaving lit bars under a
   dead handle. */
#hpp-main-parent-container .hpp-seek.hpp-seek-disabled ~ .hpp-seek-segs,
#sgp-main-parent-container .hpp-seek.hpp-seek-disabled ~ .hpp-seek-segs,
#sdp-main-parent-container .hpp-seek.hpp-seek-disabled ~ .hpp-seek-segs { opacity: 0.4; }

/* Seek slider is inactive while a single row is looping (no defined total length). */
.hpp-seek.hpp-seek-disabled {
    opacity: 0.4;
    pointer-events: none;
}
/* Volume slider locked — the same treatment as the seek slider above, for a bar
   whose level control has nothing to act on (the Tinnitus module while the
   app-wide notch is running). Its own class: .hpp-seek-disabled is scoped to
   .hpp-seek, and these are two different sliders in the same bar. */
.hpp-vol.hpp-vol-disabled {
    opacity: 0.4;
    pointer-events: none;
}
/* Disabled transport buttons (e.g. timer modules before a session can play). */
.hpp-player-btn:disabled { opacity: 0.4; cursor: not-allowed; }
.hpp-player-row .bws-button-a:disabled { opacity: 0.5; cursor: not-allowed; }
    .aime-mode-btn.active {
        opacity: 1;
        background: var(--bg-accent-medium) !important;
        border-color: var(--color-accent) !important;
        color: var(--color-accent) !important;
        box-shadow: 0 0 6px var(--glow-accent);
    }

    .mlib-field-row {
        display: flex;
        flex-direction: column;
        gap: 0.3rem;
        margin-bottom: 0.75rem;
    }
    /* The Gemini TTS model switch. A .mlib-mode-row nested inside a field row,
       which already supplies the spacing below it — the row's own tab margin
       would double it. */
    .gm-tts-modelrow { margin-bottom: 0; }
    .mlib-two-col {
        flex-direction: row;
        gap: 1rem;
        align-items: flex-start;
    }
    .mlib-col {
        flex: 1;
        display: flex;
        flex-direction: column;
        gap: 0.3rem;
        min-width: 0;
    }

    .mlib-toggle-wrap {
        display: flex;
        align-items: center;
        gap: 0.6rem;
        cursor: pointer;
        user-select: none;
        margin-top: 0.2rem;
    }
    .mlib-slider-row {
        display: flex;
        align-items: center;
        gap: 0.75rem;
    }
    @keyframes aime-pulse-border {
        0%, 100% { box-shadow: 0 0 6px var(--glow-accent); }
        50% { box-shadow: 0 0 18px var(--glow-accent-heavy); }
    }

    .mlib-status-row {
        display: flex;
        align-items: center;
        gap: 0.6rem;
        flex: 1;
        justify-content: flex-end;
        min-width: 0;
    }
    @keyframes aime-spin { to { transform: rotate(360deg); } }
    .mlib-auth-notice svg { stroke: rgba(255,152,0,0.9); flex-shrink: 0; }

/* ───── from ASMR-MODULE/index.html ───── */
/* Title bar */
    #asmr-section-playlist { margin-top: 1.5rem; }

    /* ── Presets: tabs + grid (mirrors Frequency Presets) ─────────────────── */
    #asmr-main-parent-container .alb-tabs,
    #gm-main-parent-container .alb-tabs,
    .mlib-tabs,
    #medm-main-parent-container .alb-tabs,
    #nat-main-parent-container .alb-tabs,
    #spc-main-parent-container .alb-tabs { display: flex; gap: 8px; margin-bottom: 1rem; }
    #asmr-main-parent-container .alb-tab,
    #bp-presets-parent-container .bp-tab,
    #fp-presets-parent-container .fp-tab,
    #gm-main-parent-container .alb-tab,
    #ip-presets-parent-container .ip-tab,
    #medm-main-parent-container .alb-tab,
    #nat-main-parent-container .alb-tab,
    #spc-main-parent-container .alb-tab {
        flex: 1; padding: 0.6rem 1rem; border-radius: 8px; cursor: pointer;
        background: var(--bg-dark-lighter); color: var(--text-muted);
        border: 1px solid var(--border-accent-light); font-size: 14px; font-weight: 500;
        transition: background .2s ease, color .2s ease, border-color .2s ease;
    }
    #asmr-main-parent-container .alb-tab.active { background: var(--color-accent); color: #111; border-color: var(--color-accent); }

    #asmr-main-parent-container .alb-grid,
    .mlib-track-list,
    #gm-main-parent-container .alb-grid,
    #medm-main-parent-container .alb-grid,
    #nat-main-parent-container .alb-grid,
    #spc-main-parent-container .alb-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 12px; }
    @media (max-width: 900px) { #asmr-main-parent-container .alb-grid { grid-template-columns: repeat(2, 1fr); } }
    @media (max-width: 600px) { #asmr-main-parent-container .alb-grid { grid-template-columns: 1fr; } }

    #asmr-main-parent-container .alb-card {
        position: relative; display: flex; gap: 10px; min-height: 160px; cursor: pointer; overflow: hidden;
        background: var(--bg-dark-lighter); border: 1px solid var(--border-accent-light); border-radius: 4px;
        transition: border-color .2s ease, box-shadow .2s ease;
    }
    #asmr-main-parent-container .alb-card:hover { border-color: var(--color-accent); box-shadow: 0 0 8px var(--glow-accent); }
    #asmr-main-parent-container .alb-card-cover { width: 84px; flex-shrink: 0; object-fit: cover; background: #111; align-self: stretch; }
    #asmr-main-parent-container .alb-card-body { flex: 1; min-width: 0; padding: 10px 10px 10px 0; display: flex; flex-direction: column; gap: 4px; }
    #asmr-main-parent-container .alb-card-top { display: flex; align-items: flex-start; gap: 6px; }
    #asmr-main-parent-container .alb-card-title { flex: 1; font-size: 14px; font-weight: 600; color: var(--text-primary, #fff); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
    /* .alb-info-btn look comes from .bws-info-badge */
    .alb-badge,
    .bp-badge,
    .fp-badge,
    .ip-badge,
    .mlib-badge,
    .prb-badge { align-self: flex-start; padding: 2px 6px; border-radius: 4px; font-size: 9px; font-weight: 700; letter-spacing: 0.5px; color: rgba(255,255,255,0.85); }
    #asmr-main-parent-container .alb-card-desc { font-size: 12px; color: var(--text-bright, rgba(255,255,255,0.8)); line-height: 1.4; display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; }
    #asmr-main-parent-container .alb-card-empty { border: 1px dashed var(--border-accent-light); border-radius: 4px; min-height: 160px; opacity: 0.22; }

    .alb-badge-free { background: rgba(75,200,100,0.25); }
    .alb-badge-basic { background: rgba(75,150,255,0.25); }
    .alb-badge-premium { background: rgba(180,75,255,0.25); }
    .alb-badge-paid { background: rgba(255,216,75,0.18); }

    #asmr-main-parent-container .alb-pager,
    .mlib-pager { display: flex; align-items: center; justify-content: center; gap: 14px; margin-top: 1rem; }
    #asmr-main-parent-container .alb-pager button { padding: 6px 14px; border-radius: 4px; cursor: pointer; font-weight: 600; background: var(--bg-dark-lighter); border: 1px solid var(--border-accent-light); color: var(--color-accent); }
    #asmr-main-parent-container .alb-pager button:disabled { opacity: 0.3; cursor: not-allowed; }
    #asmr-main-parent-container .alb-pager-info { font-size: 13px; color: var(--text-muted); }
    #asmr-main-parent-container .alb-personal-empty,
    #asmr-main-parent-container .alb-grid-msg { grid-column: 1 / -1; text-align: center; color: var(--text-muted); padding: 2rem 1rem; font-size: 14px; line-height: 1.6; }

    /* ── Card info modals (cover/title/badge/description + track list) ─────── */
    .alb-modal-overlay,
    .av2p-dialog-overlay,
    .bp-modal-overlay,
    .fp-modal-overlay,
    .ip-modal-overlay,
    .it-info-overlay { position: fixed; inset: 0; z-index: 100000; display: flex; align-items: center; justify-content: center; padding: 24px; }
    /* ── COVER-ON-TOP INFO MODAL TEMPLATE ───────────────────────────────────
       One shape for every info modal opened from a card in a preset/pack/album
       grid: a 16:9 cover banner on top, then a scrolling body, then a fixed
       action footer. .alb-modal (albums, packs, personal playlists) is the
       reference; .bp-/.fp-/.ip-modal (binaural, frequency, isochronic presets,
       plus the breathwork, media-library, singing-bowls and presets-builder
       modals that ride on .fp-modal) are the same design and share it.

       Not part of this template: .bt-/.ft-/.it-info-modal and .sbp-bowl-info-
       modal. Those open from a dropdown's info button, not from a card, and are
       carousels over a picker's options rather than one card's detail — they are
       sized for their own content (720px, 800px) and .bt-/.ft-/.it- put a 1:1
       image in a 260px side column instead of a banner on top, a shape this rule
       would fight rather than fix.

       600px to match the cover art: the catalog images in R2 are 600px wide, so
       the cover sits at its native size instead of being upscaled from 400. The
       overlay keeps a 24px gutter and width:100% takes over below ~648px, so a
       phone still gets a full-bleed-minus-gutter modal.

       max-height + flex column is what makes the cover clamp below actually
       work: without a height budget the box grows past the viewport and there is
       no share for the clamp to take a third of. */
    .alb-modal,
    .bp-modal,
    .fp-modal,
    .ip-modal { width: 100%; max-width: 600px; max-height: calc(100vh - 100px); display: flex; flex-direction: column; border: 1px solid var(--color-accent); border-radius: 4px; overflow: hidden; box-shadow: 0 0 16px var(--glow-accent); }
    /* 16:9 like the cards — see the note on .plp-pcard-cover. flex-shrink: 0 so
       the cover keeps its 16:9 height and the body is what yields; the clamp
       below is the only thing that ever takes height off it. */
    .alb-modal-cover-wrap,
    .bp-modal-cover-wrap,
    .fp-modal-cover-wrap,
    .ip-modal-cover-wrap { position: relative; width: 100%; height: auto; aspect-ratio: 16 / 9; flex-shrink: 0; min-height: 0; overflow: hidden; }
    /* min-height: 0 is what makes the 16:9 hold for a cover that is not 16:9.
       The wrap is a flex item in a column flex container, so min-height: auto is
       the content-based minimum — the image's own intrinsic height. A 1:1 cover
       (Suno returns square artwork, unlike the 16:9 ones the pack builder makes)
       would push the wrap to a square and quietly beat the aspect-ratio; with the
       minimum switched off the ratio wins and .alb-modal-cover's object-fit: cover
       takes the crop off the top and bottom. */

    /* Short-viewport guard. The modal is capped at calc(100vh - 100px) and the
       cover does not shrink, so on a laptop or a landscape phone a full-height
       16:9 cover crowds the track list into a sliver. Below 1000px tall the
       cover is clamped to a third of the modal's height budget and gives up the
       difference top and bottom (.alb-modal-cover is object-fit: cover, so it
       crops rather than squashes), leaving two thirds to the content.

       max-height, never height, so the cover is only ever made smaller than its
       16:9 height — it is never stretched or zoomed to fill a share it does not
       need. The two numbers are chosen to meet: a third of the budget at 1000px
       tall is 300px, already under the 337.5px a 600px-wide 16:9 cover wants, so
       the clamp bites from the first pixel below the threshold rather than
       sitting inert through a dead zone. From there the crop deepens smoothly as
       the window shrinks, and the split holds at 33/67.

       The 140px floor stops that from running away: a strict third of the budget
       on a landscape phone would leave a letterbox sliver with most of the image
       thrown away, so below roughly 520px tall the cover holds 140px and takes
       the larger share instead. max() means the floor only ever applies at the
       very bottom of the range; everywhere above it the third still governs. */
    @media (max-height: 1000px) {
        .alb-modal-cover-wrap,
        .bp-modal-cover-wrap,
        .fp-modal-cover-wrap,
        .ip-modal-cover-wrap { max-height: max(140px, calc((100vh - 100px) / 3)); }
    }
    .alb-modal-cover,
    .bp-modal-cover,
    .fp-modal-cover,
    .ip-modal-cover { width: 100%; height: 100%; object-fit: cover; background: #111; display: block; }
    .alb-modal-close,
    .bp-modal-close,
    .fp-modal-close,
    .ip-modal-close { position: absolute; top: 10px; right: 10px; width: 28px; height: 28px; border-radius: 50%; cursor: pointer; font-size: 13px; line-height: 1; background: rgba(0,0,0,0.65); border: 1px solid var(--border-accent-light); color: #fff; }
    .alb-modal-body { padding: 14px 18px 4px; overflow-y: auto; }
    .alb-modal-namerow,
    .bp-modal-namerow,
    .fp-modal-namerow,
    .ip-modal-namerow { display: flex; align-items: flex-start; gap: 10px; margin-bottom: 10px; }
    .alb-modal-name,
    .bp-modal-name,
    .fp-modal-name,
    .ip-modal-name { flex: 1; font-size: 16px; font-weight: 700; color: #fff; line-height: 1.35; }
    .alb-modal-desc { font-size: 13px; line-height: 1.55; color: rgba(248,249,250,0.8); margin-bottom: 10px; }
    .alb-modal-tracks { border-top: 1px solid rgba(255,216,75,0.15); padding-top: 8px; }
    .alb-modal-tracks-head { display: flex; justify-content: space-between; font-size: 11px; font-weight: 700; letter-spacing: 0.04em; text-transform: uppercase; color: var(--color-accent); margin-bottom: 6px; }
    .alb-modal-tracks-loading,
    .alb-modal-tracks-empty { font-size: 12px; color: var(--text-muted); padding: 6px 0; }
    .alb-modal-track { display: flex; align-items: center; gap: 8px; padding: 4px 0; font-size: 12px; }
    .alb-modal-track-num { flex-shrink: 0; width: 18px; text-align: right; color: var(--text-muted); }
    .alb-modal-track-name { flex: 1; min-width: 0; color: rgba(255,255,255,0.88); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
    .alb-modal-track-dur { flex-shrink: 0; color: var(--text-muted); font-variant-numeric: tabular-nums; }
    .alb-modal-action { padding: 16px; flex-shrink: 0; border-top: 1px solid var(--color-border, rgba(255,255,255,0.08)); }
    .alb-modal-load,
    .alb-modal-goto { width: 100%; padding: 11px; border-radius: 4px; cursor: pointer; font-size: 13px; font-weight: 700; border: 1px solid #d9b53c; background: #d9b53c; color: #000; transition: background-color .2s ease, border-color .2s ease; }
    .alb-modal-load:hover:not(:disabled),
    .alb-modal-goto:hover {
        background: var(--color-accent, #ffd84b); border-color: var(--color-accent, #ffd84b);
    }
    .alb-modal-load:disabled,
    .av2p-dialog-save:disabled,
    .bp-save-confirm:disabled,
    .fp-save-confirm:disabled,
    .ip-save-confirm:disabled { opacity: 0.5; cursor: default; }
    .alb-modal-loaded,
    .bp-modal-loaded,
    .fp-modal-loaded,
    .ip-modal-loaded { text-align: center; color: var(--color-accent); font-weight: 600; font-size: 13px; padding: 6px 0 10px; }
    .alb-modal-note,
    .bp-modal-note,
    .fp-modal-note,
    .ip-modal-note { text-align: center; font-size: 12px; color: var(--text-muted); padding: 4px 0 10px; }

    /* Info-modal meta row, built from the same duration/date pills the card
       carries (.bws-meta-badge), so a modal looks like the card it opened
       from. Every preset/album info modal uses this — there is no second
       treatment. */
    .bws-modal-pills { display: flex; flex-wrap: wrap; gap: 7px; margin: 0 0 12px; }

    /* Album modal: per-track "Add As Track" button with its playlist-position
       input. (Its two playlist buttons are the card's — .plp-pcard-loads.) */
    .alb-modal-addtrack { flex-shrink: 0; margin-left: 4px; padding: 2px 7px; border-radius: 3px; cursor: pointer; font-size: 10px; font-weight: 700; white-space: nowrap; border: 1px solid var(--border-accent-light); background: rgba(255,216,75,0.08); color: var(--color-accent); }
    .alb-modal-addtrack:disabled { opacity: 0.6; cursor: default; }
    .alb-modal-pos { flex-shrink: 0; width: 34px; padding: 2px 3px; text-align: center; font-size: 11px; font-variant-numeric: tabular-nums; border-radius: 3px; border: 1px solid var(--color-border, rgba(255,255,255,0.15)); background: rgba(0,0,0,0.4); color: #fff; -moz-appearance: textfield; appearance: textfield; }
    .alb-modal-pos::-webkit-outer-spin-button,
    .alb-modal-pos::-webkit-inner-spin-button { -webkit-appearance: none; margin: 0; }


    /* ── Album edit dialog, admin only (js/plp-album-admin.js) ────────────
       The catalog twin of the personal save/edit dialog, and deliberately the
       same object: it wears .fp-save-dialog and .bws-cover-dialog, so the name,
       description, tag chips, cover block and button row are not restyled here
       at all. What follows is only the part a personal playlist has no
       equivalent of — the track list — plus the disk-upload row beneath the AI
       cover block and the error line above the buttons. */
    .alb-ae-dialog { max-width: 620px; }

    .alb-ae-tracks-section { margin-top: 14px; }
    /* Scrolls rather than growing: a Nature Sounds album runs to dozens of
       tracks, and a dialog taller than the window would put Save off-screen. */
    .alb-ae-tracks {
        max-height: 260px;
        overflow-y: auto;
        border: 1px solid var(--color-border, rgba(255,255,255,0.15));
        border-radius: 4px;
        padding: 4px 8px;
        background: rgba(0,0,0,0.25);
    }
    .alb-ae-track { display: flex; align-items: center; gap: 8px; padding: 4px 0; font-size: 12px; }
    .alb-ae-track + .alb-ae-track { border-top: 1px solid rgba(255,255,255,0.06); }
    .alb-ae-track-num { flex-shrink: 0; width: 18px; text-align: right; color: var(--text-muted); font-variant-numeric: tabular-nums; }
    .alb-ae-track-name { flex: 1; min-width: 0; color: rgba(255,255,255,0.88); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
    .alb-ae-track-dur { flex-shrink: 0; color: var(--text-muted); font-variant-numeric: tabular-nums; }
    .alb-ae-track-empty { font-size: 12px; line-height: 1.5; color: var(--color-danger, #f44336); padding: 8px 2px; }

    /* One button shape for ↑ ↓ ✕ and + Add, so the row reads as a set of moves
       rather than four different controls. */
    .alb-ae-track-btn {
        flex-shrink: 0;
        min-width: 24px;
        padding: 2px 6px;
        border-radius: 3px;
        cursor: pointer;
        font-size: 11px;
        font-weight: 700;
        line-height: 1.4;
        white-space: nowrap;
        border: 1px solid var(--border-accent-light);
        background: rgba(255,216,75,0.08);
        color: var(--color-accent);
        transition: background-color .15s ease, border-color .15s ease, color .15s ease;
    }
    .alb-ae-track-btn:hover:not(:disabled) { background: rgba(255,216,75,0.2); }
    .alb-ae-track-btn:disabled { opacity: 0.35; cursor: default; }
    /* Removing is the one move that takes something away, so it is the one that
       turns red — and only on hover, so the list is not a row of warnings. */
    .alb-ae-track-rm:hover:not(:disabled) { background: #f44336; border-color: #f44336; color: #fff; }

    .alb-ae-add-toggle { margin-top: 8px; }
    .alb-ae-add { margin-top: 8px; }
    .alb-ae-add-list {
        max-height: 200px;
        overflow-y: auto;
        border: 1px solid var(--color-border, rgba(255,255,255,0.15));
        border-radius: 4px;
        padding: 4px 8px;
        background: rgba(0,0,0,0.25);
    }
    .alb-ae-add-row { display: flex; align-items: center; gap: 8px; padding: 4px 0; font-size: 12px; }
    .alb-ae-add-row + .alb-ae-add-row { border-top: 1px solid rgba(255,255,255,0.06); }
    .alb-ae-add-note { font-size: 12px; line-height: 1.5; color: var(--text-muted); padding: 6px 2px; }

    /* The same list and picker on a user's saved playlist (js/plp-personal.js).
       Its pool spans the user's own tracks and every album they may play, so it
       gets a search box and a heading per source. */
    .plp-ed-add-search {
        width: 100%;
        box-sizing: border-box;
        margin-bottom: 6px;
        padding: 5px 8px;
        font-size: 12px;
        border-radius: 4px;
        border: 1px solid var(--color-border, rgba(255,255,255,0.15));
        background: rgba(0,0,0,0.25);
        color: inherit;
    }
    /* Two-level accordion inside the picker — sources (YOUR TRACKS /
       BRAINWAVESOUNDS TRACKS), then groups (unused, then one per playlist or
       album). One of each open at a time, so the box holds a single list rather
       than every album's tracks at once — hence the extra height here. */
    .alb-ae-dialog .alb-ae-add-list { max-height: 320px; }
    /* Add tracks… on the left, the "Replace playlist" box filling the rest of
       the row; the picker opens beneath both. Wraps onto two lines on a phone. */
    .plp-ed-actions {
        display: flex;
        flex-wrap: wrap;
        align-items: center;
        gap: 10px;
        margin-top: 8px;
    }
    .plp-ed-actions .alb-ae-add-toggle { margin-top: 0; flex-shrink: 0; }
    /* Add tracks… is a toggle: while the picker is open it wears the shared
       .bws-toggle-btn "ON" look (the Step Edit Preview buttons) — accent
       border, text and glow — so it reads as switched on, not just hovered. */
    .bws-cover-dialog .prb-action-btn.alb-ae-add-toggle.active {
        background: var(--border-accent-light);
        border-color: var(--color-accent);
        color: var(--color-accent);
        box-shadow: 0 0 8px var(--glow-accent);
    }
    .plp-ed-actions .prb-edit-replace { flex: 1 1 260px; min-width: 0; margin: 0; }
    /* Spacing in the edit dialog, one rhythm: 10px between the list, the
       Add/Replace row and the picker; 16px from whichever of them is last down
       to Cancel / Save changes, the same breathing room the cover block above
       leaves. The generic dialog input rule gives every field a 12px bottom
       margin, so the search field is pulled back to the 10px step here. */
    .alb-ae-dialog .alb-ae-tracks-section { margin: 14px 0 16px; }
    .alb-ae-dialog .plp-ed-actions { margin-top: 10px; }
    .alb-ae-dialog .alb-ae-add { margin-top: 10px; }
    .alb-ae-dialog .plp-ed-search-wrap .plp-ed-add-search { margin-bottom: 8px; }
    /* Ticking "Replace playlist" overrides the list, so the list and the
       picker step aside; the Add button and the replace box stay. */
    .alb-ae-tracks-section.is-replacing .alb-ae-tracks,
    .alb-ae-tracks-section.is-replacing .alb-ae-add { display: none; }

    /* Looking glass inside the picker's search field, before the placeholder. */
    .plp-ed-search-wrap { position: relative; }
    .plp-ed-search-ico {
        position: absolute;
        left: 8px;
        top: 50%;
        transform: translateY(calc(-50% - 4px)); /* centred on the field, not its 8px bottom margin */
        color: var(--text-muted);
        pointer-events: none;
    }
    .plp-ed-search-wrap .plp-ed-add-search { padding-left: 28px; }
    .plp-ed-sec-head,
    .plp-ed-grp-head {
        display: flex;
        align-items: center;
        gap: 8px;
        width: 100%;
        padding: 6px 2px;
        border: 0;
        background: none;
        cursor: pointer;
        text-align: left;
        color: inherit;
        font: inherit;
    }
    .plp-ed-sec + .plp-ed-sec { border-top: 1px solid rgba(255,255,255,0.1); }
    .plp-ed-sec-head {
        font-size: 12px;
        font-weight: 700;
        letter-spacing: .05em;
        text-transform: uppercase;
        color: var(--color-accent);
    }
    .plp-ed-sec-body { padding: 0 0 6px 10px; }
    .plp-ed-grp-head {
        font-size: 11px;
        font-weight: 600;
        letter-spacing: .03em;
        text-transform: uppercase;
        color: rgba(255,255,255,0.75);
    }
    .plp-ed-grp + .plp-ed-grp { border-top: 1px solid rgba(255,255,255,0.06); }
    .plp-ed-grp-body { padding: 0 0 4px 10px; }
    .plp-ed-head-label { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
    .plp-ed-count { flex-shrink: 0; font-size: 11px; font-weight: 400; color: var(--text-muted); font-variant-numeric: tabular-nums; }
    .plp-ed-caret { flex-shrink: 0; display: inline-flex; transform: rotate(-90deg); transition: transform .15s ease; }
    .is-open > .plp-ed-sec-head .plp-ed-caret,
    .is-open > .plp-ed-grp-head .plp-ed-caret { transform: none; }
    .plp-ed-sec-head:hover .plp-ed-head-label,
    .plp-ed-grp-head:hover .plp-ed-head-label { text-decoration: underline; }

    /* Disk upload, under the AI cover block it is an alternative to. */
    .alb-ae-upload { margin-top: 10px; }
    .alb-ae-upload-name { margin-left: 8px; font-size: 11px; color: var(--color-accent); }
    .alb-ae-upload-note { margin-top: 5px; font-size: 11px; line-height: 1.5; color: var(--text-muted); }

    /* Sits above the buttons, empty until something fails — so the reason a save
       did not happen appears where the eye already is, not in an alert. */
    .alb-ae-err {
        display: none;
        margin-top: 10px;
        font-size: 12px;
        line-height: 1.5;
        color: var(--color-danger, #f44336);
    }

    /* ── "In Playlist" on a track's info modal ────────────────────────────
       Rides on the same .alb-modal-tracks list the clip rows use; all this adds
       is the collapsed header's affordances and the destructive hover on the
       row action, which removes rather than adds. */
    .bws-inpl-head { align-items: center; cursor: pointer; user-select: none; }
    /* Same yellow row-tint as the visualizer's param-group heads. The padding
       gives the tint room around the text; the negative margin cancels it so
       the title stays aligned with the rows below. */
    .alb-modal-tracks-head.bws-inpl-head {
        padding: 0.45rem 0.5rem;
        margin: 0 -0.5rem 6px;
        border-radius: 4px;
    }
    .bws-inpl-head:hover { background: rgba(255, 216, 75, 0.08); }
    .bws-inpl-caret { display: flex; transition: transform .18s ease; }
    .bws-inpl-head.is-open .bws-inpl-caret { transform: rotate(180deg); }
    .bws-inpl-rm:hover:not(:disabled) { background: #f44336; border-color: #f44336; color: #fff; }

    /* ── Personal playlist presets (Personal tab) — shared by the Meditation
       Music / Nature Sounds / Guided Meditations / Special / ASMR modules.
       Save dialog reuses the shared .fp-save-dialog / .fp-tags-* styles. ──── */
    .plp-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 12px; }
    @media (max-width: 900px) { .plp-grid { grid-template-columns: repeat(2, 1fr); } }
    @media (max-width: 560px) { .plp-grid { grid-template-columns: 1fr; } }
    .plp-msg { grid-column: 1 / -1; text-align: center; color: var(--text-muted); padding: 2rem 1rem; font-size: 14px; line-height: 1.6; }
    /* Filler slot on a short last page, so the 3x4 grid keeps its shape — the
       .alb-card-empty of the albums tab, sized for the taller personal card
       (120px cover + body). */
    .plp-card-empty { border: 1px dashed var(--border-accent-light); border-radius: 4px; min-height: 220px; opacity: 0.22; }
    /* Pager under the Personal grid — mirrors .alb-pager on the albums tab. */
    .plp-pager { display: flex; align-items: center; justify-content: center; gap: 14px; margin-top: 1rem; }
    .plp-pager:empty { display: none; }
    .plp-pager button { padding: 6px 14px; border-radius: 4px; cursor: pointer; font-weight: 600; background: var(--bg-dark-lighter); border: 1px solid var(--border-accent-light); color: var(--color-accent); }
    .plp-pager button:disabled { opacity: 0.3; cursor: not-allowed; }
    .plp-pager-info { font-size: 13px; color: var(--text-muted); }
    /* position: relative anchors the ✎ badge pinned over the cover. */
    .plp-pcard { position: relative; display: flex; flex-direction: column; min-height: 220px; cursor: pointer; border: 1px solid var(--border-accent-light); border-radius: 4px; overflow: hidden; background: var(--bg-dark-lighter); }
    .plp-pcard:hover { border-color: var(--color-accent); box-shadow: 0 0 8px var(--glow-accent); }
    /* 16:9, app-wide: a cover is shown at its own shape rather than cropped to a
       fixed band. Some grids' art is square (the AI track / recording covers ask
       bws-cover-ai for 1:1) and those do lose their top and bottom here — that is
       the accepted trade for one card shape everywhere. height:auto is required,
       since a definite height wins over aspect-ratio.
       The compact (cover-left) view resets this — see [data-bws-cardview="side"]. */
    .plp-pcard-cover { width: 100%; height: auto; aspect-ratio: 16 / 9; object-fit: cover; background: #111; display: block; }
    .plp-pcard-cover-ph { background: var(--bg-dark-lighter, #1a1a1a); }
    .plp-pcard-body { flex: 1; padding: 10px 12px 12px; display: flex; flex-direction: column; gap: 4px; }
    /* The album card's .alb-card-top: title and ⓘ on one line. The min-height is
       what the badge would impose anyway — spelled out so a card without a badge
       (an AI track) still gives the title the same room. */
    .plp-pcard-top { display: flex; align-items: flex-start; gap: 6px; min-height: 24px; }
    .plp-pcard-title { flex: 1; font-size: 14px; font-weight: 600; color: var(--text-primary, #fff); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
    .plp-pcard-desc { font-size: 12px; color: var(--text-bright, rgba(255,255,255,0.8)); line-height: 1.4; display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; }
    .plp-pcard-actions { display: flex; gap: 8px; margin-top: auto; padding-top: 6px; }
    /* Darker yellow at rest, accent yellow under the cursor — the same two-tone
       as .alb-modal-load and the entry cards' create button, so a grid of these
       does not read as a wall of lit buttons. */
    .plp-pcard-load {
        flex: 1; padding: 7px; border-radius: 5px; cursor: pointer; font-size: 12px; font-weight: 700;
        border: 1px solid #d9b53c; background: #d9b53c; color: #000;
        transition: background-color .2s ease, border-color .2s ease;
    }
    .plp-pcard-load:hover { background: var(--color-accent, #ffd84b); border-color: var(--color-accent, #ffd84b); }
    .plp-pcard-load:disabled { cursor: default; }
    /* "Load as new playlist" over "Add to active playlist", on every playlist
       card — saved (Personal) and Brainwavesounds album alike. The column takes
       the width; a saved playlist's delete sits beside it, level with the
       bottom button. */
    .plp-pcard-actions { align-items: flex-end; }
    .plp-pcard-loads { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 6px; }
    /* Both buttons spell their type out in full. A <button> does not inherit the
       page font, so without this the two could render in the browser's own
       face and line height and look mismatched beside each other. */
    .plp-pcard-loads .plp-pcard-load {
        flex: none; width: 100%;
        font-family: inherit; font-size: 13px; font-weight: 500; line-height: 1.3;
    }
    /* The secondary of the two wears the filter bar's Favorites / Clear shell:
       grey at rest, accent border and text under the cursor, never filled — so
       the pair reads as one primary action and its alternative. */
    .plp-pcard-loads .plp-pcard-append {
        border: 1px solid var(--border-accent-light, rgba(255,216,75,0.25));
        background: var(--bg-dark-light, #141414);
        color: var(--text-muted, #aaa);
        font-size: 12px;
        font-weight: 400;
    }
    .plp-pcard-loads .plp-pcard-append:hover:not(:disabled) {
        background: var(--bg-dark-light, #141414);
        border-color: var(--color-accent, #ffd84b);
        color: var(--color-accent, #ffd84b);
    }
    /* The same pair in the album / saved-playlist info modal, which opens with
       them disabled until the track list has arrived — dimmed so that wait
       shows. (On a card, disabled only means "working", and the button's own
       text already says so.) */
    .alb-modal-action .plp-pcard-load:disabled { opacity: 0.5; }
    /* Album cards gain the row at the foot of their body. */
    .alb-card-actions { margin-top: auto; padding-top: 6px; }
    .plp-pcard-del { padding: 7px 10px; border-radius: 5px; cursor: pointer; font-size: 12px; font-weight: 600; border: 1px solid var(--border-accent-light); background: var(--bg-dark-lighter, #1a1a1a); color: var(--color-accent); }
    .plp-pcard-del:hover { background: #f44336; border-color: #f44336; color: #fff; }

    /* The bowl-type · frequency line on a Singing Bowls session card and its info
       modal. Unscoped, unlike the instrument one below: the class name is already
       unique to this module, and the modal is appended to <body> where a
       container-scoped selector would not reach it. */
    .scp-type-row {
        display: flex; flex-wrap: wrap; align-items: center; gap: 7px;
        font-size: 11px; font-weight: 600; text-transform: uppercase; letter-spacing: 0.3px;
        color: var(--color-accent, #ffd84b);
    }
    .scp-type-dot {
        flex: 0 0 auto; width: 4px; height: 4px; border-radius: 50%;
        background: var(--color-accent, #ffd84b);
    }
    .fp-modal .scp-type-row { margin: 0 0 10px; }

    /* The type · scale line on an instrument composition card and its info modal
       — the Handpan / Gong / Shamanic Drum grids. It is the one row of those
       cards that is not the shared .plp-pcard's, and it is id-scoped because the
       three modules name it after themselves. */
    #handpan-composition-presets-container .hcp-pcard-type,
    #gong-composition-presets-container    .gcp-pcard-type,
    #drum-composition-presets-container    .dcp-pcard-type,
    .alb-modal .hcp-pcard-type,
    .alb-modal .gcp-pcard-type,
    .alb-modal .dcp-pcard-type {
        display: flex; flex-wrap: wrap; align-items: center; gap: 7px;
        font-size: 11px; font-weight: 600; text-transform: uppercase; letter-spacing: 0.3px;
        color: var(--color-accent, #ffd84b);
    }
    #handpan-composition-presets-container .hcp-pcard-dot,
    #gong-composition-presets-container    .gcp-pcard-dot,
    #drum-composition-presets-container    .dcp-pcard-dot,
    .alb-modal .hcp-pcard-dot,
    .alb-modal .gcp-pcard-dot,
    .alb-modal .dcp-pcard-dot {
        flex: 0 0 auto; width: 4px; height: 4px; border-radius: 50%;
        background: var(--color-accent, #ffd84b);
    }
    /* The info modal's type line sits under the name, above the pills. */
    .alb-modal .hcp-pcard-type,
    .alb-modal .gcp-pcard-type,
    .alb-modal .dcp-pcard-type { margin: 0 0 10px; }

    /* Entry card pinned to the front of an album module's personal grids (today:
       Meditation Music's AI Music Creator). Same tile as a saved playlist so the
       row reads as one grid, with the builder cards' yellow glyph cover and
       action button — those two come from the shared .bws-card-cover--custom /
       .bws-card-create-btn rules, which this class is listed in. */
    .plp-pcard-custom { min-height: 220px; }
    .plp-pcard-custom .bws-card-create-btn { margin-top: auto; }

    /* ── Playlist segment (mirrors My Library Playlist) ───────────────────── */
    /* No gap: the bars between the cards carry the spacing themselves, so a single
       boundary can drop it (a pause is flush against the track it belongs to). */
    .alb-playlist-list,
    .mlib-playlist-list { display: flex; flex-direction: column; gap: 0; }
    /* One-line status above the clip list: a clip that could not load, an output
       that would not start, clips an export had to leave out. Hidden until there
       is something to say (plNotice in js/plp-album.js sets display). */
    .alb-pl-note { display: none; font-size: 12px; line-height: 1.35; color: var(--color-warning, #ff9800); background: rgba(255,152,0,0.08); border: 1px solid rgba(255,152,0,0.35); border-radius: 4px; padding: 0.5rem 0.65rem; margin-bottom: 0.5rem; }
    .alb-pl-empty { font-size: 12px; color: var(--text-muted, rgba(255,255,255,0.4)); padding: 0.85rem; text-align: center; border: 1px dashed var(--color-border, rgba(255,255,255,0.12)); border-radius: 4px; }
    .alb-pl-card { background: var(--bg-dark-medium, rgba(255,255,255,0.04)); border: 1px solid var(--color-border, rgba(255,255,255,0.1)); border-radius: 4px; overflow: hidden; }
    /* Two states, one look apart. ACTIVE is the row a press of master play would
       start (cued, stopped): the solid yellow border. PLAYING keeps that border but
       breathes — a slow glow fade — so a glance tells cued from running. */
    .alb-pl-card--active,
    .mlib-pl-card--active,
    .alb-pl-card--playing,
    .mlib-pl-card--playing { border-color: var(--color-accent, #ffd84b); box-shadow: 0 0 0 1px rgba(255,216,75,0.4), 0 0 10px rgba(255,216,75,0.2); }
    /* …over a faint golden wash, a shade above black. OPAQUE on purpose: a
       translucent tint would pick up the page's background sweep, so the top row
       and the bottom row of the same list would not read as the same colour. */
    .alb-pl-card--playing,
    .mlib-pl-card--playing { background: #17140b; animation: bws-pl-glow 2.6s ease-in-out infinite; }
    @keyframes bws-pl-glow {
        0%, 100% { border-color: rgba(255,216,75,0.5);  box-shadow: 0 0 0 1px rgba(255,216,75,0.22), 0 0 5px rgba(255,216,75,0.10); }
        50%      { border-color: rgba(255,216,75,1);    box-shadow: 0 0 0 1px rgba(255,216,75,0.75), 0 0 18px rgba(255,216,75,0.45); }
    }
    /* The drag/drop edge markers are box-shadows too — the animation would eat
       them, so a row being dragged over stops breathing until the drop lands. */
    .alb-pl-card.alb-pl-dragging,
    .alb-pl-card.alb-pl-drop-above,
    .alb-pl-card.alb-pl-drop-below,
    .mlib-pl-card.mlib-pl-dragging,
    .mlib-pl-card.mlib-pl-drop-above,
    .mlib-pl-card.mlib-pl-drop-below { animation: none; }
    @media (prefers-reduced-motion: reduce) {
        .alb-pl-card--playing,
        .mlib-pl-card--playing { animation: none; }
    }
    .alb-pl-card.alb-pl-dragging { opacity: 0.45; }
    .alb-pl-card.alb-pl-drop-above { box-shadow: inset 0 3px 0 0 var(--color-accent, #ffd84b); }
    .alb-pl-card.alb-pl-drop-below { box-shadow: inset 0 -3px 0 0 var(--color-accent, #ffd84b); }
    .alb-pl-top,
    .mlib-pl-top { display: flex; align-items: center; gap: 0.5rem; padding: 0.35rem 0.6rem; }
    .alb-pl-drag-handle { flex-shrink: 0; display: flex; align-items: center; padding: 0 0.1rem; color: rgba(255,216,75,0.35); cursor: grab; transition: color 0.15s; }
    .alb-pl-drag-handle:hover { color: rgba(255,216,75,0.85); }
    .alb-pl-drag-handle:active { cursor: grabbing; }
    .alb-pl-num,
    .mlib-pl-num,
    .ft-timer-num,
    .bt-timer-num,
    .it-timer-num { flex-shrink: 0; width: 20px; height: 20px; display: flex; align-items: center; justify-content: center; border-radius: 50%; background: var(--color-accent, #ffd84b); color: #000; font-size: 11px; font-weight: 700; }
    .alb-pl-thumb,
    .mlib-pl-thumb { width: 35px; height: 35px; object-fit: cover; border-radius: 4px; border: 1px solid rgba(255,255,255,0.1); flex-shrink: 0; cursor: pointer; transition: border-color .15s ease; }
    /* Clickable: opens the track's info modal. */
    .mlib-pl-thumb:hover,
    .mlib-pl-thumb:focus-visible { border-color: var(--color-accent, #ffd84b); outline: none; }
    /* Fixed 20-character slot, never flexible: the row is one line now, and a
       title that grew with the name would start the controls at a different x on
       every row. The text is already cut to 20 characters in JS (17 + "..."), so
       the ellipsis here is only a guard for a wide glyph run. */
    .alb-pl-title,
    .mlib-pl-title { flex: 0 0 20ch; width: 20ch; min-width: 20ch; max-width: 20ch; font-size: 13px; color: var(--color-text, rgba(255,255,255,0.9)); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
    .alb-pl-remove,
    .mlib-pl-remove { flex-shrink: 0; display: flex; align-items: center; justify-content: center; width: 24px; height: 24px; padding: 0; border-radius: 50%; border: 1px solid rgba(220,60,60,.5); background: rgba(220,60,60,.12); color: rgba(220,60,60,.9); cursor: pointer; transition: all 0.15s; }
    .alb-pl-remove:hover,
    .mlib-pl-remove:hover { background: rgba(220,60,60,.3); border-color: rgba(220,60,60,.9); color: #ff5050; }

    /* Row player control bar */
    .alb-player { display: flex; align-items: center; gap: 0.55rem; padding: 0.45rem 0.75rem; background: var(--bg-dark-heavy, rgba(0,0,0,0.3)); border-top: 1px solid var(--color-border, rgba(255,255,255,0.08)); }
    /* …and inside a playlist row it is not a band at all: it sits between the
       title and the ✕ on the row's own line, so it drops the background, the
       separator and the padding and just claims the free width. */
    .alb-pl-top > .alb-player,
    .alb-pl-top > .alb-pause-player,
    .mlib-pl-top > .mlib-player,
    .mlib-pl-top > .mlib-pause-player {
        flex: 1 1 auto; min-width: 0; padding: 0; background: none; border: 0; border-radius: 0;
    }
    /* The ✕ is the row's last item, hard right, whatever sits before it. */
    .alb-pl-top > .alb-pl-remove,
    .mlib-pl-top > .mlib-pl-remove { margin-left: auto; }
    .alb-play-btn:hover,
    .ft-pause-play:hover,
    .bt-pause-play:hover,
    .it-pause-play:hover { background: rgba(255,216,75,0.14); }
    /* Seek + IN/OUT chevron range track (per-clip start/stop trim) */
    .alb-seek-col,
    .mlib-seek-col { flex: 3; min-width: 0; display: flex; flex-direction: column; gap: 1px; }
    .alb-seek-col .alb-seek,
    .mlib-seek-col .mlib-seek { flex: 0 0 auto; width: 100%; }
    /* Passed-time fill clamped to the chevron window: track color before IN, accent only IN→position */
    .alb-seek-col .alb-seek { background: linear-gradient(to right, var(--slider-track-bg, rgba(255,255,255,0.15)) 0%, var(--slider-track-bg, rgba(255,255,255,0.15)) var(--in, 0%), var(--color-accent) var(--in, 0%), var(--color-accent) var(--val, 0%), var(--slider-track-bg, rgba(255,255,255,0.15)) var(--val, 0%)) !important; }
    .alb-seek-range,
    .mlib-seek-range { position: relative; height: 11px; }
    .alb-seek-range-span,
    .mlib-seek-range-span { position: absolute; top: 0; height: 2px; background: rgba(255,216,75,0.35); border-radius: 1px; pointer-events: none; }
    .alb-seek-handle,
    .mlib-seek-handle { position: absolute; top: 1px; width: 0; height: 0; border-left: 6px solid transparent; border-right: 6px solid transparent; border-bottom: 9px solid rgba(255,216,75,0.65); transform: translateX(-50%); cursor: ew-resize; touch-action: none; }
    .alb-seek-handle:hover, .alb-seek-handle:active { border-bottom-color: #ffd84b; }
    .alb-seek-bubble,
    .mlib-seek-bubble { position: absolute; top: -19px; transform: translateX(-50%); padding: 1px 5px; font-size: 10px; line-height: 1.4; font-variant-numeric: tabular-nums; white-space: nowrap; color: #ffd84b; background: rgba(15,15,15,0.95); border: 1px solid rgba(255,216,75,0.6); border-radius: 3px; pointer-events: none; z-index: 5; }
    .alb-trim-dur { color: #ffd84b !important; }

    .alb-pl-transport,
    .mlib-pl-transport { display: flex; align-items: center; gap: 0.55rem; margin-top: 0.85rem; padding: 0.5rem 0.75rem; background: var(--bg-dark-heavy, rgba(0,0,0,0.3)); border: 1px solid var(--border-accent-medium); border-radius: 4px; }
    .alb-pl-transport .alb-pl-seek-wrap,
    .mlib-pl-transport .mlib-pl-seek-wrap { flex: 3; min-width: 0; position: relative; display: flex; align-items: center; }
    .alb-pl-transport .alb-pl-seek-wrap .alb-pl-seek,
    .mlib-pl-transport .mlib-pl-seek-wrap .mlib-pl-seek { flex: 1; min-width: 0; width: auto; position: relative; z-index: 1; background: transparent !important; }
    .alb-pl-transport .alb-pl-master-slider { flex: 1; min-width: 0; width: auto; }
    .alb-pl-ticks,
    .mlib-pl-ticks { position: absolute; left: 0; right: 0; top: 50%; height: 6px; transform: translateY(-50%); display: flex; align-items: center; gap: 1px; pointer-events: none; }
    /* One rule for every segmented position bar — playlists, timers, and the
       copies the Media Mixer overlays on its channel sliders. BWSSegBar puts
       `bws-seg` on every span (js/bws-seg-bar.js); the prefixed classes ride along
       for module-specific tweaks. */
    .bws-seg { flex: 1 1 0%; min-width: 2px; height: 6px; border-radius: 3px; background: linear-gradient(to right, var(--color-accent) 0%, var(--color-accent) var(--segfill, 0%), var(--slider-track-bg, rgba(255,255,255,0.15)) var(--segfill, 0%)); }
    .alb-pl-master-pct,
    .mlib-pl-master-pct { flex-shrink: 0; font-size: 11px; color: var(--text-muted, rgba(255,255,255,0.55)); min-width: 34px; text-align: right; }

    /* ── Playlist modules: silent "pause" gaps ─────────────────────────────── */
    /* Progress bar: a pause segment reads as silence — a hatched base with a
       muted slate fill for elapsed, visually distinct from the yellow music. */
    /* The hatch lives in one token because four places draw it: the playlist
       modules' own bars, the timer modules' session bars, and the copies of both
       that the Media Mixer overlays on its channel sliders. --segfill resolves on
       whichever element uses the token, so the elapsed portion still fills. */
    :root {
        --bws-pause-seg-bg:
            linear-gradient(to right, rgba(150,162,186,0.9) 0%, rgba(150,162,186,0.9) var(--segfill, 0%), transparent var(--segfill, 0%)),
            repeating-linear-gradient(45deg, rgba(255,255,255,0.11) 0 3px, rgba(255,255,255,0.03) 3px 6px);
    }
    .bws-seg--pause { background: var(--bws-pause-seg-bg); }
    /* Pause row: a dimmer fill so it's not mistaken for a track — same solid
       border as every other card. Opaque for the same reason the playing wash is:
       a translucent fill would show the page's background sweep through, so two
       gaps at opposite ends of the list would not match. The :not() keeps this
       off the playing row, which owns the accent fill above. */
    .alb-pl-card--pause:not(.alb-pl-card--playing),
    .mlib-pl-card--pause:not(.mlib-pl-card--playing) { background: #121212; }
    /* The pause disc keeps the numbered rows' accent badge — only the glyph inside
       is sized down so it sits inside the 20px circle rather than filling it. */
    .alb-pl-pause-glyph svg,
    .mlib-pl-pause-glyph svg { width: 11px; height: 11px; }
    /* Keep "Pause" at natural width so the H:M:S boxes sit right beside it,
and
       push the remove button to the far right. */
    /* "Pause" takes its natural width — the track rows' fixed 20ch slot would leave
       a long blank between the word and the H:M:S boxes that belong next to it. */
    .alb-pl-card--pause .alb-pl-title,
    .mlib-pl-card--pause .mlib-pl-title {
        flex: 0 0 auto; width: auto; min-width: 0; max-width: none;
        color: var(--color-accent, #ffd84b);
    }
    .alb-pl-card--pause .alb-pl-remove,
    .mlib-pl-card--pause .mlib-pl-remove { margin-left: auto; }
    .ft-dur-chip,
    .bt-dur-chip,
    .it-dur-chip { font-size: 10px; padding: 0.12rem 0.45rem; border-radius: 20px; border: 1px solid var(--color-border, rgba(255,255,255,0.15)); background: transparent; color: var(--text-muted, rgba(255,255,255,0.6)); cursor: pointer; transition: all 0.15s; }
    .ft-dur-chip:hover,
    .bt-dur-chip:hover,
    .it-dur-chip:hover { border-color: rgba(255,216,75,0.6); color: var(--color-accent, #ffd84b); }
    .ft-dur-chip.active,
    .bt-dur-chip.active,
    .it-dur-chip.active { background: var(--color-accent, #ffd84b); border-color: var(--color-accent, #ffd84b); color: #000; font-weight: 700; }
    /* HH:MM:SS entry (0–99 each). It sits directly after the "Pause" title — the
       row's only duration display,
which is why it carries no leading label. */
       .alb-pause-custom,
       .mlib-pause-custom,
       .ft-pause-custom,
       .bt-pause-custom,
       .it-pause-custom { display: flex; align-items: center; gap: 0.25rem; flex-shrink: 0; }

    /* Timer modules: the session transport while the mix is incomplete (an empty row in
       any timer). The Play button carries its own :disabled state; this greys the rest
       of the bar — seek, skip, mute, master volume — and takes it out of reach. */
    .ft-transport-blocked .hpp-player, .bt-transport-blocked .hpp-player, .it-transport-blocked .hpp-player { opacity: 0.45; pointer-events: none; }
    /* …except the play button, which stays reachable so hovering it can explain the
       block and pressing it can name the rows still to fill. It is dimmed by the bar
       around it, so it must not carry .bws-soft-disabled's opacity as well. */
    .ft-transport-blocked .hpp-play-btn, .bt-transport-blocked .hpp-play-btn, .it-transport-blocked .hpp-play-btn { pointer-events: auto; cursor: not-allowed; }
    /* Looks disabled, still listens. For controls outside the dimmed bar. */
    .bws-soft-disabled { opacity: 0.45; cursor: not-allowed; }

    /* Why the session won't start, listing the rows that are still empty. Shared by the
       three timer modules — the dialog is word-for-word the same in each, so it carries
       no module prefix. Scrim and glass come from the two frosted-modal rules below. */
    .bws-playblock-overlay { position: fixed; inset: 0; z-index: 100000; display: flex; align-items: center; justify-content: center; padding: 24px; }
    .bws-playblock-modal {
        position: relative; width: 100%; max-width: 460px; max-height: calc(100vh - 100px); overflow-y: auto;
        border: 1px solid var(--color-accent); border-radius: 6px; padding: 18px;
        box-shadow: 0 0 16px var(--glow-accent);
        font-family: 'Roboto',-apple-system,BlinkMacSystemFont,"Segoe UI","Helvetica Neue",sans-serif;
    }
    .bws-playblock-modal h3 { margin: 0 0 10px; font-size: 16px; font-weight: 700; color: #fff; line-height: 1.35; }
    .bws-playblock-msg { margin: 0; font-size: 13px; line-height: 1.5; color: var(--color-text, rgba(255,255,255,0.9)); }
    .bws-playblock-list { margin: 0.75rem 0 0; padding: 0; list-style: none; display: flex; flex-wrap: wrap; gap: 0.3rem; }
    .bws-playblock-list li {
        font-size: 11px; font-weight: 600; padding: 0.12rem 0.45rem; border-radius: 20px; white-space: nowrap;
        background: rgba(255,216,75,0.12); border: 1px solid rgba(255,216,75,0.3); color: var(--color-accent, #ffd84b);
    }
    .bws-playblock-ok { width: 100%; margin-top: 16px; font-size: 14px; font-weight: 500; }
    .bws-playblock-close {
        position: absolute; top: 12px; right: 12px; width: 28px; height: 28px; flex-shrink: 0; padding: 0;
        border: 0; border-radius: 50%; cursor: pointer;
        background: rgba(211,211,211,1); color: rgba(20,20,20,0.95);
        display: flex; align-items: center; justify-content: center;
    }
    .bws-playblock-close:hover { background: var(--color-danger); color: #fff; }

    /* A playlist pause row is one line: handle, glyph, "Pause", the H:M:S boxes and
       then its position bar, with the ✕ hard right. The quick-pick chips are gone
       from the playlists (the timers still have theirs, below). */
    .alb-pl-card--pause .alb-pl-top,
    .mlib-pl-card--pause .mlib-pl-top { flex-wrap: nowrap; }
    /* A timer pause card is one line as well: glyph, "Pause", the H:M:S boxes,
       then the position bar, with the ✕ hard right. */
    .ft-timer--pause .ft-pause-top,
    .bt-timer--pause .bt-pause-top,
    .it-timer--pause .it-pause-top { flex-wrap: nowrap; }
    .alb-pause-hms,
    .mlib-pause-hms,
    .ft-pause-hms,
    .bt-pause-hms,
    .it-pause-hms,
    .mex-dur-input,
    .sbp-dur-input,
    .tin-dur-input { width: 2.9em; text-align: center; font-size: 12px; font-variant-numeric: tabular-nums; padding: 0.15rem 0.2rem; color: var(--color-text, rgba(255,255,255,0.9)); background: var(--bg-dark-heavy, rgba(0,0,0,0.3)); border: 1px solid var(--color-border, rgba(255,255,255,0.15)); border-radius: 4px; -moz-appearance: textfield; appearance: textfield; }
    .alb-pause-hms::-webkit-inner-spin-button,
    .mlib-pause-hms::-webkit-inner-spin-button,
    .alb-pause-hms::-webkit-outer-spin-button,
    .mlib-pause-hms::-webkit-outer-spin-button,
    .ft-pause-hms::-webkit-inner-spin-button,
    .ft-pause-hms::-webkit-outer-spin-button,
    .bt-pause-hms::-webkit-inner-spin-button,
    .bt-pause-hms::-webkit-outer-spin-button,
    .it-pause-hms::-webkit-inner-spin-button,
    .it-pause-hms::-webkit-outer-spin-button,
    .mex-dur-input::-webkit-inner-spin-button,
    .mex-dur-input::-webkit-outer-spin-button,
    .sbp-dur-input::-webkit-inner-spin-button,
    .sbp-dur-input::-webkit-outer-spin-button,
    .tin-dur-input::-webkit-inner-spin-button,
    .tin-dur-input::-webkit-outer-spin-button { -webkit-appearance: none; margin: 0; }
    .alb-pause-hms:focus,
    .mlib-pause-hms:focus,
    .ft-pause-hms:focus,
    .bt-pause-hms:focus,
    .it-pause-hms:focus,
    .mex-dur-input:focus,
    .sbp-dur-input:focus,
    .tin-dur-input:focus { outline: none; border-color: var(--color-accent, #ffd84b); }
    .alb-pause-colon,
    .mlib-pause-colon,
    .ft-pause-colon,
    .bt-pause-colon,
    .it-pause-colon,
    .mex-dur-colon,
    .sbp-dur-colon,
    .tin-dur-colon { font-size: 12px; color: var(--text-muted, rgba(255,255,255,0.5)); }
    /* Position bar: play, 0:00 … total slider. It rides on the pause row itself
       (see .alb-pl-top > .alb-pause-player above), so no separator, no band. */
    .alb-pause-player,
    .mlib-pause-player,
    .ft-pause-player,
    .bt-pause-player,
    .it-pause-player { display: flex; align-items: center; gap: 0.55rem; flex: 1 1 auto; min-width: 0; }
    .alb-pause-seek,
    .mlib-pause-seek,
    .ft-pause-seek,
    .bt-pause-seek,
    .it-pause-seek { flex: 1; min-width: 0; }

    /* Inter-row insert bars: a round accent disc with a pause glyph in every gap
       (before the first row,
       between rows,
after the last) — the primary way to add
       a pause. Same button family as the add/duplicate pairs in the timer and
       composition-builder modules; the margin gives the disc room to sit on the
       separator line without crowding the rows above and below. */
    /* The bar itself takes no room: it is a zero-height line between two cards, and
       the disc is hung off it so it straddles the border of the card above (the
       first bar straddles the top border of the first card). No separator rule —
       the card borders are the only lines the list needs. pointer-events are off
       on the bar so only the 21px disc, which overhangs both cards, takes clicks. */
    .alb-pl-inserter,
    .mlib-pl-inserter { position: relative; height: 0.25rem; margin: 0; z-index: 3; pointer-events: none; }

    /* A pause belongs to the track above it: no bar space between the two, so the
       gap card is flush against its track. A LEADING pause (nothing above it) is
       not owned by the track below, so that boundary keeps its spacing. */
    .alb-pl-card:not(.alb-pl-card--pause) + .alb-pl-inserter:has(+ .alb-pl-card--pause),
    .mlib-pl-card:not(.mlib-pl-card--pause) + .mlib-pl-inserter:has(+ .mlib-pl-card--pause) { height: 0; }

    .alb-pl-inserter-btn,
    .mlib-pl-inserter-btn {
        width: 21px;
        height: 21px;
        padding: 0;
        border: 0;
        border-radius: 50%;
        display: inline-flex;
        align-items: center;
        justify-content: center;
        cursor: pointer;
        position: absolute;
        /* Left edge, in the drag-handle column: row padding-left (0.6rem) plus
           half the handle box (10px glyph + 0.1rem padding a side) — so the disc
           lines up under the handle of the card above it, not mid-row. */
        left: 16px;
        top: 0;
        transform: translate(-50%, -50%);
        pointer-events: auto;
        z-index: 1;
        /* Darker yellow at rest, accent on hover, colour-only — same pair as the
           row add/duplicate discs this sits alongside. */
        background: #d9b53c;
        color: #111;
        transition: background-color .2s ease;
    }
    /* The first bar has no card above it, so its disc hangs off the bar's BOTTOM
       edge instead — i.e. on the top border of the first card. */
    .alb-pl-inserter:first-child > .alb-pl-inserter-btn,
    .mlib-pl-inserter:first-child > .mlib-pl-inserter-btn { top: 100%; }

    /* No disc on either side of a pause row: a pause after a pause is nothing, and
       the slot before one is already taken by that pause. */
    .alb-pl-card--pause + .alb-pl-inserter > .alb-pl-inserter-btn,
    .alb-pl-inserter:has(+ .alb-pl-card--pause) > .alb-pl-inserter-btn,
    .mlib-pl-card--pause + .mlib-pl-inserter > .mlib-pl-inserter-btn,
    .mlib-pl-inserter:has(+ .mlib-pl-card--pause) > .mlib-pl-inserter-btn { display: none; }
    .alb-pl-inserter-btn:hover,
    .mlib-pl-inserter-btn:hover {
        background: var(--color-accent, #ffd84b);
    }
    .alb-pl-inserter-btn svg,
    .mlib-pl-inserter-btn svg { display: block; }

/* ───── from AUDIO-VISUALIZER-MODULE-PRO/index.html ───── */
.av-stage,
.av2-stage {
        position: relative;
        width: 100%;
        aspect-ratio: 16 / 9;
        background: #000;
        border: 1px solid rgba(255, 216, 75, 0.2);
        border-radius: 8px;
        overflow: hidden;
        margin-bottom: 1rem;
    }
    #av-canvas,
    #av2-canvas {
        display: block;
        width: 100%;
        height: 100%;
    }
    #av-stage:fullscreen,
    #av2-stage:fullscreen { aspect-ratio: auto; border: none; border-radius: 0; }
    #av-stage:fullscreen #av-canvas,
    #av2-stage:fullscreen #av2-canvas { width: 100vw; height: 100vh; }

    .av-fps,
    .av2-fps {
        position: absolute;
        top: 8px;
        right: 10px;
        font-family: 'Courier New', monospace;
        font-size: 11px;
        color: rgba(255, 216, 75, 0.75);
        background: rgba(0, 0, 0, 0.4);
        padding: 1px 6px;
        border-radius: 3px;
        pointer-events: none;
        user-select: none;
    }

    .av-controls,
    .av2-controls {
        display: flex;
        flex-wrap: wrap;
        align-items: flex-end;
        gap: 1rem;
    }
    .av-field,
    .av2-field {
        display: flex;
        flex-direction: column;
        gap: 0.3rem;
        min-width: 150px;
    }
    .av-field .bws-label { margin: 0; }

    /* The four selects in the top row (Visualization / Filters / Quality /
       Temporal) are pinned to exactly 150px so the row's wrap points stay put
       no matter how long the option labels get. Panel fields further down keep
       the 150px floor and are still free to grow. */
    .av2-controls > .av2-field {
        flex: 0 0 150px;
        width: 150px;
        min-width: 150px;
        max-width: 150px;
    }

    /* Forces the wrap after Save Preset at every width, so the two rows are a
       fixed layout rather than a coincidence of the current viewport.

       The break is its own flex line, so the two rows end up separated by TWO
       row-gaps rather than one. Negative margins cannot claw that back — a flex
       line's cross size is floored at zero, so the shrunk item just leaves the
       gaps where they were. Set row-gap instead: it is half the space you want
       between the rows. Column spacing stays at the row's usual 1rem. */
    .av2-controls {
        column-gap: 1rem;
        row-gap: 0.375rem;
    }
    .av2-controls > .av2-row-break {
        flex: 0 0 100%;
        height: 0;
    }

    /* The top row wraps, and the plain-text buttons in it (Start, Load Image,
       Save Preset) were carrying the app-wide 2rem side padding, which is what
       pushed Save Preset onto a second line. Trim the sides here only. */
    .av2-controls .bws-button-a { padding-left: 1rem; padding-right: 1rem; }

    /* ── Per-section preset locks ─────────────────────────────────────────
       A lock in front of Filters / Quality / Temporal and each of the three
       toggle buttons. Grey = the setting follows a loaded preset; red = it is
       pinned and applyPreset() skips that section entirely. Red rather than
       the usual accent because a lock is the one control here that makes the
       app ignore what you just asked it to load. The glyph is deliberately
       small and quiet — it is a guard rail, not a control anyone needs to
       hunt for. */
    .av2-field-head { display: flex; align-items: center; gap: 4px; }
    .av2-field-head .bws-label { margin: 0; }
    .av2-lock-pair  { display: inline-flex; align-items: center; gap: 4px; }
    .av2-lock {
        flex: 0 0 auto;
        display: inline-flex; align-items: center; justify-content: center;
        width: 18px; height: 18px; padding: 0;
        border: 0; border-radius: 3px; background: none; cursor: pointer;
        color: var(--text-muted, #888);
        transition: color .15s ease, background-color .15s ease;
    }
    .av2-lock:hover { color: rgba(244, 67, 54, 0.75); background: rgba(255, 255, 255, 0.07); }
    .av2-lock[aria-pressed="true"] { color: var(--color-danger, #f44336); }
    .av2-lock:focus-visible { outline: 2px solid var(--color-accent, #ffd84b); outline-offset: 1px; }
    /* Palette is a bare select with no label of its own, so its lock shares the
       row with the dropdown. Scoped to this one container — .av2-param-select is
       also used by four panel selects that have no lock. */
    .av2-param-select.av2-lock-row { flex-direction: row; align-items: center; gap: 4px; }
    .av2-param-select.av2-lock-row .bws-dropdown { flex: 1; min-width: 0; }
    /* On a global slider the lock sits in front of the title, inside the label
       (the left-packed layout that makes that work is on .av2-param .bws-label). */
    .av2-globals .av2-param-animrow .bws-label > .av2-lock {
        align-self: center;
        width: 16px; height: 16px;
        margin-right: 1px;
    }
    .av-field #av-sensitivity-val {
        color: #ffd84b;
        font-variant-numeric: tabular-nums;
    }

    .av-status,
    .av2-status {
        margin-top: 0.75rem;
        font-size: 12px;
        color: rgba(255, 255, 255, 0.55);
    }

/* ───── from AUDIO-VISUALIZER-MODULE-PRO-2/index.html ───── */
    /* Pop-out cover: opaque panel over the (still-rendering) canvas while popped out */
    #av2-popout-cover {
        position: absolute;
        inset: 0;
        z-index: 6;
        display: flex;
        align-items: center;
        justify-content: center;
        background: #0d0d0d;
        text-align: center;
    }
    #av2-popout-cover[hidden] { display: none; }
    .av2-popout-cover-inner { display: flex; flex-direction: column; align-items: center; gap: 16px; padding: 24px; }
    .av2-popout-cover-msg { margin: 0; color: rgba(255, 255, 255, 0.6); font-size: 14px; letter-spacing: 0.02em; }
    #av2-popin-btn { font-size: 15px; padding: 10px 30px; }

    /* "Computing…" overlay shown while a shader pipeline compiles */
    .av2-computing {
        position: absolute;
        inset: 0;
        display: flex;
        flex-direction: column;
        align-items: center;
        justify-content: center;
        gap: 12px;
        background: rgba(0, 0, 0, 0.55);
        color: rgba(255, 216, 75, 0.92);
        font-size: 13px;
        letter-spacing: 0.08em;
        text-transform: uppercase;
        pointer-events: none;
        user-select: none;
        z-index: 6;
    }
    .av2-computing[hidden] { display: none; }
    .av2-computing-spinner {
        width: 34px;
        height: 34px;
        border: 3px solid rgba(255, 216, 75, 0.22);
        border-top-color: #ffd84b;
        border-radius: 50%;
        animation: av2-spin 0.8s linear infinite;
    }
    @keyframes av2-spin { to { transform: rotate(360deg); } }

    /* Corner actions on the stage (Fullscreen / Pop Out). Hidden once the stage
       is fullscreen or popped out — neither action applies there. */
    .av2-stage-actions {
        position: absolute;
        right: 10px;
        bottom: 10px;
        display: flex;
        gap: 6px;
        z-index: 10;
    }
    #av2-stage:fullscreen .av2-stage-actions,
    .av2-stage-actions[hidden] { display: none; }
    /* The pin sits in the opposite corner from the fullscreen / pop-out pair. */
    .av2-stage-actions-left { right: auto; left: 10px; }
    .av2-stage-btn[aria-pressed="true"] {
        color: #111;
        border-color: #ffd84b;
        background: rgba(255, 216, 75, 0.85);
    }
    .av2-stage-btn[aria-pressed="true"]:hover {
        color: #111;
        background: #ffd84b;
    }

    /* Pinned: the stage rides the top of the screen (under the fixed app header)
       while you scroll its controls, and lets go at the bottom of the module — the
       plain behaviour of position: sticky, whose containing block is the module
       box. Suspended while popped out, where the stage is just a cover panel.
       #audio-visualizer-pro2-content has to stop clipping for this:
       .bbp-module-toggle-animated leaves `overflow: hidden` on an open module, and
       that is a scrollport, which would pin the stage inside the module instead of
       to the viewport. `visible` is not a scrollport, so sticky resolves against
       the viewport again; the max-height cap is already off by then, so there is
       nothing left for the module to clip anyway. */
    #audio-visualizer-pro2-content.bbp-module-open { overflow: visible; }
    #av2-stage.av2-pinned:not(.av2-pin-suspended) {
        position: sticky;
        top: var(--bws-sticky-top, var(--bws-header-h, 52px));
        z-index: 15;   /* over the controls scrolling beneath, under the app header (1100) */
    }
    .av2-stage-btn {
        width: 30px;
        height: 30px;
        display: flex;
        align-items: center;
        justify-content: center;
        padding: 0;
        border-radius: 6px;
        border: 1px solid rgba(255, 216, 75, 0.3);
        background: rgba(0, 0, 0, 0.45);
        color: rgba(255, 216, 75, 0.55);
        cursor: pointer;
        transition: background 0.15s, color 0.15s, border-color 0.15s;
    }
    .av2-stage-btn:hover {
        color: #ffd84b;
        border-color: rgba(255, 216, 75, 0.6);
        background: rgba(0, 0, 0, 0.6);
    }

    /* Pull tab — only visible while the stage is fullscreen; hover it to reveal
       the controls drawer. Hidden while the drawer is open. */
    .av2-fs-tab {
        display: none;
        position: absolute;
        right: 16px;
        bottom: 0;
        width: 52px;
        height: 28px;
        align-items: center;
        justify-content: center;
        border: 1px solid rgba(255, 216, 75, 0.35);
        border-bottom: none;
        border-radius: 9px 9px 0 0;
        background: rgba(0, 0, 0, 0.45);
        color: rgba(255, 216, 75, 0.9);
        cursor: pointer;
        z-index: 11;
        transition: opacity 0.2s, background 0.2s;
    }
    #av2-stage:fullscreen .av2-fs-tab { display: flex; }
    .av2-fs-tab:hover { background: rgba(255, 216, 75, 0.16); }
    .av2-fs-tab.av2-tab-hidden { opacity: 0; pointer-events: none; }

    /* Controls drawer while fullscreen — slides up from the bottom */
    #av2-stage:fullscreen #av2-controls-wrap {
        position: absolute;
        left: 0;
        right: 0;
        bottom: 0;
        max-height: 62vh;
        overflow-y: auto;
        background: rgba(8, 8, 10, 0.86);
        border-top: 1px solid rgba(255, 216, 75, 0.25);
        padding: 1rem 1.25rem 1.25rem;
        z-index: 9;
        backdrop-filter: blur(6px);
        transform: translateY(0);
        transition: transform 0.28s ease;
    }
    #av2-stage:fullscreen #av2-controls-wrap.av2-hidden { transform: translateY(100%); }

    .av2-field .bws-label { margin: 0; }

    .av2-globals {
        display: grid;
        grid-template-columns: repeat(auto-fit, minmax(190px, 1fr));
        /* Bottom-aligned: a title that wraps to two lines makes its cell taller,
           and stretched cells would then centre their contents, leaving the
           sliders in a grid row at different heights. Aligning to the end pins
           every slider and range track to the same baseline and lets the labels
           stack upward instead. */
        align-items: end;
        gap: 0.65rem 1.25rem;
        margin-top: 1rem;
        padding: 0.75rem 1rem;
        /* Same shell as .bws-module-content elsewhere in the app */
        border: 1px solid var(--border-accent-medium, rgba(255, 216, 75, 0.15));
        border-radius: 4px;
        background: var(--bg-dark-heavy, rgba(0, 0, 0, 0.6));
    }

    .av2-param {
        display: flex;
        flex-direction: column;
        gap: 0.25rem;
        min-width: 0;
    }
    .av2-param .bws-label {
        margin: 0;
        display: flex;
        /* Packed left, NOT space-between: rows that carry the ∞ loop mark have a
           third item in the label, and space-between would spread it into the
           middle of the row. The value takes the right edge on its own margin. */
        justify-content: flex-start;
        align-items: baseline;
        gap: 0.5rem;
        font-size: 12px;
    }
    .av2-param .bws-label > span:last-child { margin-left: auto; }
    .av2-param .bws-label span {
        color: #ffd84b;
        font-variant-numeric: tabular-nums;
        font-size: 12px;
    }
    .av2-param input[type="range"] { width: 100%; }
    /* Palette dropdown sits in the main-parameters grid like a slider cell */
    .av2-param-select .bws-dropdown { width: 100%; }
    .av2-param-select .bws-label { margin: 0; }

    /* IN/OUT animation-range track below each parameter slider */
    .av2-range-track {
        position: relative;
        height: 11px;
        margin-top: 1px;
    }
    .av2-range-span {
        position: absolute;
        top: 0;
        height: 2px;
        background: rgba(255, 216, 75, 0.35);
        border-radius: 1px;
        pointer-events: none;
    }
    .av2-range-handle {
        position: absolute;
        top: 1px;
        width: 0;
        height: 0;
        border-left: 6px solid transparent;
        border-right: 6px solid transparent;
        border-bottom: 9px solid rgba(255, 216, 75, 0.65);
        transform: translateX(-50%);
        cursor: ew-resize;
        touch-action: none;
    }
    .av2-range-handle:hover,
    .av2-range-handle:active { border-bottom-color: #ffd84b; }

    /* Param row with the ∿ animation toggle in front of the slider */
    /* A global param whose row is built by the shared row builder sits in a slot
       div. display:contents lets the built row itself be the grid item, so it
       lines up with the hand-written global params beside it; an inline
       display:none on the slot still hides the row when the style has no video. */
    .av2-param-slot { display: contents; }

    .av2-param-animrow {
        flex-direction: row;
        align-items: center;
        gap: 0.55rem;
    }
    .av2-param-main {
        flex: 1;
        display: flex;
        flex-direction: column;
        gap: 0.25rem;
        min-width: 0;
    }
    .av2-anim-toggle {
        flex: 0 0 auto;
        width: 26px;
        height: 26px;
        border-radius: 6px;
        border: 1px solid rgba(255, 216, 75, 0.3);
        background: rgba(0, 0, 0, 0.35);
        color: rgba(255, 216, 75, 0.55);
        font-size: 15px;
        line-height: 1;
        cursor: pointer;
        padding: 0;
        transition: background 0.15s, color 0.15s, box-shadow 0.15s;
    }
    .av2-anim-toggle:hover { color: #ffd84b; border-color: rgba(255, 216, 75, 0.6); }
    .av2-anim-toggle.active {
        background: rgba(255, 216, 75, 0.22);
        color: #ffd84b;
        border-color: #ffd84b;
        box-shadow: 0 0 6px rgba(255, 216, 75, 0.45);
    }
    /* ∞ seamless-loop mark — sits after the slider's title when sweeping the
       slider min→max returns to the identical image, so a ∿ Loop on it cannot
       seam. Informational only: not a control, so it takes no pointer events.
       The .cond variant is loopable only at Detail Falloff 0 with a whole-number
       Complexity, because it feeds the LOD-weighted per-iteration rotation. */
    .av2-loop-mark {
        display: inline-block;
        /* The label is a flex row with a 0.5rem gap, and the title text node ends
           in a space — both sit between the title and this mark. Pull it back so
           the ∞ reads as part of the title rather than as a separate column. */
        margin-left: -0.35rem;
        color: rgba(255, 216, 75, 0.75);
        font-size: 1.05em;
        line-height: 1;
        vertical-align: -0.04em;
        cursor: help;
        pointer-events: auto;
    }
    .av2-loop-mark.cond {
        color: rgba(255, 216, 75, 0.38);
    }

    /* ♪ audio-react toggle — a cyan accent so it reads apart from ∿ */
    .av2-audio-toggle {
        color: rgba(90, 224, 255, 0.6);
        border-color: rgba(90, 224, 255, 0.3);
        font-size: 13px;
    }
    .av2-audio-toggle:hover { color: #5ae0ff; border-color: rgba(90, 224, 255, 0.6); }
    .av2-audio-toggle.active {
        background: rgba(90, 224, 255, 0.2);
        color: #5ae0ff;
        border-color: #5ae0ff;
        box-shadow: 0 0 6px rgba(90, 224, 255, 0.45);
    }

    .av2-params-section {
        margin-top: 1rem;
        border: 1px solid var(--border-accent-medium, rgba(255, 216, 75, 0.15));
        border-radius: 4px;
        background: var(--bg-dark-heavy, rgba(0, 0, 0, 0.6));
        overflow: hidden;
    }
    .av2-params-header {
        display: flex;
        align-items: center;
        gap: 0.65rem;
        padding: 0.55rem 1rem;
        border-bottom: 1px solid var(--border-accent-medium, rgba(255, 216, 75, 0.15));
    }
    .av2-params-title {
        flex: 1;
        font-size: 14px;
        color: rgba(255, 255, 255, 0.85);
    }
    .av2-params-title #av2-params-name { color: #ffd84b; }
    .av2-params-caret {
        background: none;
        border: none;
        color: rgba(255, 216, 75, 0.85);
        cursor: pointer;
        padding: 0;
        display: flex;
        align-items: center;
    }
    .av2-params-caret .caret-up { display: none; }
    .av2-params-caret .caret-down { display: block; }
    .av2-params-caret.active .caret-up { display: block; }
    .av2-params-caret.active .caret-down { display: none; }
    .av2-reset-btn { padding: 0.25rem 0.9rem; font-size: 12px; }

    .av2-params-grid {
        display: grid;
        grid-template-columns: repeat(auto-fit, minmax(210px, 1fr));
        align-items: end;          /* see .av2-globals — wrapped titles must not offset the sliders */
        gap: 0.7rem 1.25rem;
        padding: 0.85rem 1rem 1rem;
    }
    .av2-params-grid.av2-collapsed { display: none; }

    /* Category containers — grid switches to a vertical stack of grouped sections */
    .av2-params-grid.av2-grouped:not(.av2-collapsed) {
        display: block;
        padding: 0.6rem 0.75rem 0.85rem;
    }
    .av2-param-group {
        border: 1px solid var(--border-accent-medium, rgba(255, 216, 75, 0.15));
        border-radius: 4px;
        background: rgba(0, 0, 0, 0.35);
        margin-top: 0.6rem;
        overflow: hidden;
    }
    .av2-param-group:first-child { margin-top: 0; }
    .av2-param-group-head {
        display: flex;
        align-items: center;
        gap: 0.5rem;
        padding: 0.45rem 0.85rem;
        cursor: pointer;
        font-size: 11px;
        letter-spacing: 0.08em;
        text-transform: uppercase;
        color: rgba(255, 216, 75, 0.85);
        background: rgba(0, 0, 0, 0.4);
        user-select: none;
    }
    .av2-param-group-head:hover { background: rgba(255, 216, 75, 0.08); }
    .av2-param-group-head .av2-group-name { flex: 1; }
    .av2-param-group-head .av2-group-caret {
        font-size: 12px;
        transition: transform 0.15s;
    }
    .av2-param-group.collapsed .av2-group-caret { transform: rotate(-90deg); }
    .av2-param-group-body {
        display: grid;
        grid-template-columns: repeat(auto-fit, minmax(210px, 1fr));
        align-items: end;          /* see .av2-globals — wrapped titles must not offset the sliders */
        gap: 0.7rem 1.25rem;
        padding: 0.75rem 0.85rem 0.85rem;
    }
    .av2-param-group.collapsed .av2-param-group-body { display: none; }

    /* ── Visualizer Presets section (tabs + grid, mirrors Meditation Presets) ── */
    #av2-section-presets { margin-bottom: 1.5rem; }
    #av2-section-presets .av2p-tabs { display: flex; gap: 8px; margin-bottom: 1rem; }
    #av2-section-presets .av2p-tab {
        flex: 1; padding: 0.6rem 1rem; border-radius: 8px; cursor: pointer;
        background: var(--bg-dark-lighter, rgba(255,255,255,0.05)); color: var(--text-muted, rgba(255,255,255,0.6));
        border: 1px solid var(--border-accent-light, rgba(255,216,75,0.2)); font-size: 14px; font-weight: 500;
        transition: background .2s ease, color .2s ease, border-color .2s ease;
    }
    #av2-section-presets .av2p-tab.active { background: var(--color-accent, #ffd84b); color: #111; border-color: var(--color-accent, #ffd84b); }

    #av2-section-presets .av2p-grid { display: grid; grid-template-columns: repeat(4, 1fr); gap: 12px; }
    @media (max-width: 1100px) { #av2-section-presets .av2p-grid { grid-template-columns: repeat(3, 1fr); } }
    @media (max-width: 820px)  { #av2-section-presets .av2p-grid { grid-template-columns: repeat(2, 1fr); } }
    @media (max-width: 560px)  { #av2-section-presets .av2p-grid { grid-template-columns: 1fr; } }

    /* Pager under each grid — same shape as the meditation-music module's. */
    #av2-section-presets .av2p-pager { display: flex; align-items: center; justify-content: center; gap: 14px; margin-top: 1rem; }
    #av2-section-presets .av2p-pager:empty { display: none; }
    #av2-section-presets .av2p-pager button { padding: 6px 14px; border-radius: 4px; cursor: pointer; font-weight: 600; font-family: inherit; background: var(--bg-dark-lighter); border: 1px solid var(--border-accent-light); color: var(--color-accent); }
    #av2-section-presets .av2p-pager button:disabled { opacity: 0.3; cursor: not-allowed; }
    #av2-section-presets .av2p-pager-info { font-size: 13px; color: var(--text-muted); }

    #av2-section-presets .av2p-card {
        position: relative; display: flex; flex-direction: column; cursor: pointer; overflow: hidden;
        background: var(--bg-dark-lighter, rgba(255,255,255,0.05)); border: 1px solid var(--border-accent-light, rgba(255,216,75,0.2)); border-radius: 6px;
        transition: border-color .2s ease, box-shadow .2s ease;
    }
    #av2-section-presets .av2p-card:hover { border-color: var(--color-accent, #ffd84b); box-shadow: 0 0 8px var(--glow-accent, rgba(255,216,75,0.35)); }
    #av2-section-presets .av2p-card-cover {
        width: 100%; aspect-ratio: 16 / 9; object-fit: cover; background: #0a0a0c; display: block;
        border-bottom: 1px solid rgba(255,216,75,0.12);
    }
    #av2-section-presets .av2p-card-cover-empty { display: flex; align-items: center; justify-content: center; color: rgba(255,216,75,0.35); font-size: 11px; }
    #av2-section-presets .av2p-card-body { padding: 8px 10px 10px; display: flex; flex-direction: column; gap: 2px; min-width: 0; }
    #av2-section-presets .av2p-card-name { font-size: 13px; font-weight: 600; color: var(--text-primary, #fff); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
    #av2-section-presets .av2p-card-vis { font-size: 11px; color: var(--text-muted, rgba(255,255,255,0.55)); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
    #av2-section-presets .av2p-card-del {
        position: absolute; top: 6px; right: 6px; width: 24px; height: 24px; padding: 0; border-radius: 50%;
        display: none; align-items: center; justify-content: center; cursor: pointer; line-height: 1; font-size: 14px;
        background: rgba(220,60,60,0.85); border: 1px solid rgba(255,255,255,0.25); color: #fff;
    }
    #av2-section-presets .av2p-card:hover .av2p-card-del { display: flex; }
    #av2-section-presets .av2p-card-del:hover { background: #ff4040; }
    /* Primary card action — same states as the media library's "Add to playlist"
       button: muted grey at rest, accent on hover, solid accent once the preset
       is on the stage. */
    #av2-section-presets .av2p-card-load {
        margin-top: 8px; width: 100%;
        display: inline-flex; align-items: center; justify-content: center; gap: 6px;
        padding: 8px 10px; border-radius: 6px; cursor: pointer;
        font-size: 12px; font-weight: 700; font-family: inherit; line-height: 1.3;
        border: 1px solid var(--color-border, rgba(255,255,255,0.15));
        background: rgba(255,255,255,0.04); color: var(--text-muted, rgba(255,255,255,0.5));
        transition: all 0.15s;
    }
    #av2-section-presets .av2p-card-load:hover:not(:disabled) {
        border-color: var(--color-accent, #ffd84b); color: var(--color-accent, #ffd84b);
    }
    #av2-section-presets .av2p-card-load.loaded,
    #av2-section-presets .av2p-card-load.loaded:hover {
        border-color: var(--color-accent, #ffd84b);
        background: var(--color-accent, #ffd84b); color: #000;
    }
    #av2-section-presets .av2p-card-load:disabled { opacity: 0.6; cursor: default; }

    /* Admin-only, personal cards: publish this preset to the Brainwavesounds tab.
       Outlined rather than solid accent — the card's primary action is still
       "click to load", and this must not compete with it. */
    #av2-section-presets .av2p-card-publish {
        margin-top: 8px; width: 100%; padding: 5px 8px; border-radius: 5px; cursor: pointer;
        font-size: 11px; font-weight: 600; line-height: 1.3;
        background: transparent; color: var(--color-accent, #ffd84b);
        border: 1px solid var(--border-accent-medium, rgba(255,216,75,0.4));
        transition: background .2s ease, color .2s ease, border-color .2s ease;
    }
    #av2-section-presets .av2p-card-publish:hover:not(:disabled) {
        background: var(--color-accent, #ffd84b); color: #111; border-color: var(--color-accent, #ffd84b);
    }
    #av2-section-presets .av2p-card-publish:disabled { opacity: 0.6; cursor: default; }
    #av2-section-presets .av2p-grid-msg {
        grid-column: 1 / -1; text-align: center; color: var(--text-muted, rgba(255,255,255,0.55));
        padding: 2rem 1rem; font-size: 14px; line-height: 1.6;
        border: 1px dashed var(--border-accent-light, rgba(255,216,75,0.2)); border-radius: 6px;
    }

    /* ── Save-preset name dialog ──────────────────────────────────────────── */
    .av2p-dialog-overlay { position: fixed; inset: 0; z-index: 100000; display: flex; align-items: center; justify-content: center; padding: 24px; }
    .av2p-dialog { width: 100%; max-width: 420px; border: 1px solid var(--color-accent, #ffd84b); border-radius: 8px; overflow: hidden; box-shadow: 0 0 18px var(--glow-accent, rgba(255,216,75,0.35)); }
    .av2p-dialog-preview { width: 100%; aspect-ratio: 16/9; object-fit: cover; background: #0a0a0c; display: block; border-bottom: 1px solid rgba(255,216,75,0.15); }
    .av2p-dialog-body { padding: 16px 18px 18px; }
    .av2p-dialog-title,
    .mr-modal-title { font-size: 15px; font-weight: 700; color: #fff; margin: 0 0 4px; }
    .av2p-dialog-sub { font-size: 12px; color: var(--text-muted, rgba(255,255,255,0.55)); margin: 0 0 12px; }
    .av2p-dialog input[type="text"] { width: 100%; box-sizing: border-box; padding: 10px 12px; border-radius: 6px; background: rgba(0,0,0,0.4); border: 1px solid rgba(255,216,75,0.3); color: #fff; font-size: 14px; }
    .av2p-dialog input[type="text"]:focus { outline: none; border-color: var(--color-accent, #ffd84b); }
    /* Admin-only note: an admin's save always publishes to Brainwavesounds, so this
       states what will happen rather than offering a choice. */
    .av2p-dialog-official { margin-top: 12px; font-size: 12px; line-height: 1.45; color: var(--text-muted, rgba(255,255,255,0.7)); }
    .av2p-dialog-actions { display: flex; gap: 10px; margin-top: 16px; }
    .av2p-dialog-actions button,
    .bp-save-dialog-row button,
    .fp-save-dialog-row button,
    .ip-save-dialog-row button,
    .mr-modal-actions button { flex: 1; padding: 10px; border-radius: 6px; cursor: pointer; font-size: 13px; font-weight: 700; }
    .av2p-dialog-save,
    .mr-modal-done { background: var(--color-accent, #ffd84b); color: #111; border: 1px solid var(--color-accent, #ffd84b); }
    .av2p-dialog-cancel { background: transparent; color: var(--text-muted, rgba(255,255,255,0.7)); border: 1px solid rgba(255,255,255,0.2); }
    .av2p-dialog-err { color: #ff6b6b; font-size: 12px; margin-top: 10px; min-height: 14px; }

    /* ── Audio-reactivity band picker modal ──────────────────────────────── */
/* ===== AV2 ∿ per-parameter animation dialog ===== */
    .av2-an-dialog { max-width: 440px; }
    .av2-an-dialog .av2p-dialog-title { color: #ffd84b; }
    .av2-an-curve {
        display: block; width: 100%; height: 84px; margin: 14px 0 4px;
        border-radius: 4px; background: rgba(255, 216, 75, 0.05);
    }
    .av2-an-curvelabels {
        display: flex; justify-content: space-between; align-items: baseline;
        font-size: 11px; letter-spacing: 0.06em; color: rgba(255, 216, 75, 0.6);
        margin-bottom: 6px;
    }
    .av2-an-curvelabels .av2-an-period {
        letter-spacing: 0; color: var(--text-muted, rgba(255,255,255,0.55));
        font-variant-numeric: tabular-nums;
    }
    .av2-an-lbl {
        display: flex; justify-content: space-between; align-items: baseline;
        font-size: 12px; margin: 12px 0 4px;
    }
    .av2-an-lbl span { color: #ffd84b; font-variant-numeric: tabular-nums; }
    .av2-an-dialog input[type="range"] { width: 100%; }
    .av2-an-dialog .av2-an-pattern { width: 100%; }
    .av2-an-dialog .av2-an-apply {
        background: #ffd84b; border-color: #ffd84b; color: #111;
    }

    .av2-ar-dialog { max-width: 440px; }
    .av2-ar-dialog .av2p-dialog-title { color: #5ae0ff; }
    .av2-ar-section { margin: 14px 0 18px; }
    .av2-ar-bandlabels { display: flex; justify-content: space-between; font-size: 11px; letter-spacing: 0.08em; color: rgba(90,224,255,0.75); margin-bottom: 6px; }
    /* live readout of the selected band, centred between the LOW/HIGH captions */
    .av2-ar-bandlabels .av2-ar-range { color: #5ae0ff; letter-spacing: 0; font-variant-numeric: tabular-nums; }
    /* octave ticks under the track; spans are positioned by the log mapping */
    .av2-ar-ticks { position: relative; height: 13px; margin-top: 3px; font-size: 9px; letter-spacing: 0; color: rgba(90,224,255,0.4); }
    .av2-ar-ticks span { position: absolute; top: 0; transform: translateX(-50%); white-space: nowrap; }
    /* taller, clickable version of the IN/OUT track, tinted cyan */
    .av2-ar-band { height: 16px; margin-top: 2px; background: rgba(90,224,255,0.08); border-radius: 3px; }
    .av2-ar-band .av2-ar-span { top: 7px; height: 3px; background: rgba(90,224,255,0.5); }
    .av2-ar-band .av2-range-handle { top: 3px; border-bottom-color: #5ae0ff; }
    .av2-ar-spectrum { display: block; width: 100%; height: 56px; margin-bottom: 10px; border-radius: 4px; background: rgba(90,224,255,0.05); }
    .av2-ar-lbl { display: flex; justify-content: space-between; align-items: baseline; font-size: 12px; margin: 12px 0 4px; }
    .av2-ar-lbl span { color: #5ae0ff; font-variant-numeric: tabular-nums; }
    .av2-ar-dialog input[type="range"] { width: 100%; }
    .av2-ar-dialog .av2-ar-apply { background: #5ae0ff; border-color: #5ae0ff; color: #06222b; }
    /* one-line explanation of the selected ♪ source */
    .av2-ar-srchint { margin: 6px 0 0; font-size: 11px; line-height: 1.45; color: var(--text-muted, rgba(255,255,255,0.55)); }
    /* sources that ignore a control grey it out rather than removing it, so the
       dialog keeps its shape as you switch between them */
    .av2-ar-dim { opacity: 0.35; pointer-events: none; }
    /* live readout of the driver value the parameter would actually be fed */
    .av2-ar-meterrow { display: flex; align-items: center; gap: 10px; margin: 4px 0 2px; }
    .av2-ar-meterlbl { font-size: 11px; letter-spacing: 0.08em; color: rgba(90,224,255,0.75); }
    .av2-ar-meter { flex: 1; height: 6px; border-radius: 3px; background: rgba(90,224,255,0.1); overflow: hidden; }
    .av2-ar-meterfill { height: 100%; width: 0; background: #5ae0ff; border-radius: 3px; }
    .av2-ar-autorow { display: flex; align-items: flex-start; gap: 8px; margin: 14px 0 2px; font-size: 12px; cursor: pointer; }
    .av2-ar-autorow input { margin: 2px 0 0; accent-color: #5ae0ff; flex: none; }
    .av2-ar-autorow em { font-style: normal; color: var(--text-muted, rgba(255,255,255,0.55)); }

/* ===== AV2 Text Overlay panel ===== */
    .av2text-body { display: flex; flex-direction: column; gap: 12px; padding: 12px; }
    /* The photic panel ends on a slider row, and the Rate ticks hang below their
       cell — 12px leaves both almost touching the panel edge. */
    #av2ph-body { padding-bottom: 22px; }
    /* Sliders read as their own block under the dropdown rows, so give that seam
       more than the panel's uniform 12px gap. */
    #av2ph-body .av2text-grid { margin-top: 8px; }
    /* The dropdown row is a grid, not free-flowing flex: every field gets the same
       column width and the row fills the panel left to right, so the selects line
       up with the slider grid below (same track sizing) instead of each one taking
       the width of its own longest option. */
    /* Id-scoped on purpose: .av2text-toprow's display:flex is declared later in
       this file, and at equal specificity the later rule would win. */
    #av2ph-body .av2ph-selects {
        display: grid; grid-auto-flow: column; grid-auto-columns: 1fr;
        gap: 12px 16px; align-items: end;
    }
    #av2ph-body .av2ph-selects .av2-field { min-width: 0; }
    #av2ph-body .av2ph-selects .bws-dropdown { width: 100%; }
    /* Six columns get narrow on a small panel — clip the labels rather than let
       them wrap and make one field taller than the rest. */
    #av2ph-body .av2ph-selects .bws-label {
        white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
    }
    .av2text-body.av2-collapsed { display: none; }
    .av2text-enable {
        display: inline-flex; align-items: center; gap: 6px; margin-left: auto; margin-right: 10px;
        font-size: 13px; color: var(--color-accent, #ffd84b); cursor: pointer; user-select: none; white-space: nowrap;
    }
    .av2text-enable input, .av2text-check input { accent-color: var(--color-accent, #ffd84b); cursor: pointer; }
    .av2text-toprow { display: flex; flex-wrap: wrap; align-items: flex-end; gap: 16px; }
    .av2text-transport { display: flex; align-items: center; gap: 8px; }
    .av2text-clock {
        font-variant-numeric: tabular-nums; font-size: 14px; color: var(--color-accent, #ffd84b);
        min-width: 46px; text-align: center;
    }
    .av2text-plainrow { display: flex; align-items: flex-end; gap: 14px; flex: 1; max-width: 420px; }
    .av2text-plainrow .av2text-speed { flex: 1; min-width: 160px; }
    .av2text-check {
        display: inline-flex; align-items: center; gap: 6px; font-size: 13px;
        color: #ccc; cursor: pointer; user-select: none; white-space: nowrap; padding-bottom: 4px;
    }
    .av2text-input {
        width: 100%; box-sizing: border-box; padding: 9px; border-radius: 5px; resize: vertical;
        min-height: 72px; border: 1px solid var(--border-accent-light); background: var(--bg-dark-light);
        color: #fff; font-size: 13px; font-family: inherit; line-height: 1.4;
    }
    .av2text-input:focus { outline: none; border-color: var(--color-accent, #ffd84b); }
    .av2text-grid {
        display: grid; grid-template-columns: repeat(auto-fill, minmax(190px, 1fr));
        gap: 12px 16px; align-items: end;
    }
    .av2text-checkrow { display: flex; align-items: center; gap: 14px; padding-bottom: 4px; }
    /* .av2text-check's own padding-bottom (slider-row alignment) would lift the
       Bold/Italic labels ~2px above the Color label here — neutralize it. */
    .av2text-checkrow .av2text-check { padding-bottom: 0; }
    .av2text-colorlab {
        display: inline-flex; align-items: center; gap: 6px; font-size: 13px; color: #ccc;
        cursor: pointer; user-select: none; white-space: nowrap;
    }
    .av2text-valcolor { display: inline-flex; align-items: center; gap: 6px; }
    .av2text-color {
        width: 26px; height: 18px; padding: 0; border: 1px solid var(--border-accent-light);
        border-radius: 3px; background: none; cursor: pointer;
    }
    .av2text-color::-webkit-color-swatch-wrapper { padding: 1px; }
    .av2text-color::-webkit-color-swatch { border: none; border-radius: 2px; }

/* ===== AV2 Photic Stimulation panel ===== */
    /* Icon + label button in the controls row (matches .bws-button-a sizing) */
    .av2-icon-btn { display: inline-flex; align-items: center; gap: 6px; }
    .av2-icon-btn svg { flex: 0 0 auto; }
    /* Same three-state colour scheme as the ∿ .av2-anim-toggle chips. */
    .av2-icon-btn[aria-pressed="false"] {
        border: 1px solid rgba(255, 216, 75, 0.3);
        background: rgba(0, 0, 0, 0.35);
        color: rgba(255, 216, 75, 0.55);
        transition: background 0.15s, color 0.15s, box-shadow 0.15s;
    }
    .av2-icon-btn[aria-pressed="false"]:hover {
        background: rgba(0, 0, 0, 0.35);
        color: #ffd84b;
        border-color: rgba(255, 216, 75, 0.6);
    }
    .av2-icon-btn[aria-pressed="true"] {
        background: rgba(255, 216, 75, 0.22);
        color: #ffd84b;
        border-color: #ffd84b;
        box-shadow: 0 0 6px rgba(255, 216, 75, 0.45);
    }
    .av2ph-readout {
        margin-left: 8px; font-size: 12px; color: var(--color-accent, #ffd84b);
        font-variant-numeric: tabular-nums;
    }
    .av2ph-readout.av2ph-free { color: var(--text-muted, rgba(255,255,255,0.55)); }
    .av2ph-readout.av2ph-warnline { color: var(--color-warning, #ff9800); }
    /* Live pulse mirror — driven per frame from the same value the shader gets, so
       the rate and the lock are visible without watching the canvas. */
    .av2ph-dot {
        display: inline-block; width: 9px; height: 9px; border-radius: 50%;
        margin-left: 6px; vertical-align: middle; opacity: 0.18;
        background: var(--color-accent, #ffd84b);
        box-shadow: 0 0 6px var(--glow-accent, rgba(255,216,75,0.4));
    }
    .av2ph-warn {
        display: flex; align-items: flex-start; gap: 10px;
        font-size: 12px; line-height: 1.5; color: var(--color-warning, #ff9800);
        background: rgba(255, 152, 0, 0.08); border: 1px solid rgba(255, 152, 0, 0.35);
        border-radius: 6px; padding: 8px 10px;
    }
    .av2ph-warn-x {
        flex: 0 0 auto; background: none; border: none; cursor: pointer; padding: 0 2px;
        color: inherit; opacity: 0.7; font-size: 13px; line-height: 1;
    }
    .av2ph-warn-x:hover { opacity: 1; }
    /* Rows the current Effect / Pulse shape doesn't use are hidden, not disabled —
       a slider that cannot do anything is noise. */
    .av2-param.av2ph-hidden { display: none; }
    /* Measured refresh, shown in the "Match display refresh" label so the option
       states the number it is matching. */
    .av2ph-snap-hz { opacity: 0.7; font-variant-numeric: tabular-nums; }
    /* Rate is inert while the isochronic tone is driving the light — dimmed and
       labelled, rather than removed, so the value stays readable. */
    .av2-param.av2ph-locked { opacity: 0.5; }
    .av2-param.av2ph-locked input[type="range"] { cursor: not-allowed; }
    .av2ph-rate-note { opacity: 0.75; font-weight: 400; text-transform: none; }
    /* Rates the display can flash evenly (base ÷ whole frames). Inset by the
       thumb's radius so a tick sits under the thumb centre, not beside it.
       Taken out of flow and hung in the row gap: the grid is bottom-aligned, so
       an in-flow strip would make this one cell taller and lift its slider out
       of line with the rest of the row. */
    #av2ph-rate-row { position: relative; }
    .av2ph-ticks {
        position: absolute; top: 100%; left: 8px; right: 8px;
        height: 7px; margin-top: 1px; pointer-events: none;
    }
    .av2ph-ticks i {
        position: absolute; top: 0; width: 1px; height: 5px;
        margin-left: -0.5px; background: currentColor;
        color: var(--text-muted, rgba(255,255,255,0.55)); opacity: 0.35;
    }
    /* Armed: the slider actually lands on these, so they read as stops, not hints. */
    .av2ph-ticks.av2ph-ticks-armed i {
        height: 7px; color: var(--color-accent, #ffd84b); opacity: 0.65;
    }

/* ===== AV2 3D Stereo panel ===== */
    /* Same rule as the photic rows: side-by-side controls and anaglyph controls
       are hidden for the mode that cannot use them. Applies to whole fields and
       checkbox labels here, not only .av2-param rows. */
    .av2st-hidden { display: none !important; }
    /* Shown in place of the controls for visualizations that don't move their
       camera per eye — informational, not a warning, so it stays neutral. */
    .av2st-note {
        font-size: 12.5px; line-height: 1.55; color: var(--text-muted, rgba(255,255,255,0.62));
        background: var(--bg-dark-light, rgba(0,0,0,0.22));
        border: 1px solid var(--border-accent-light); border-radius: 6px; padding: 10px 12px;
    }
    .av2st-note strong { color: var(--color-accent, #ffd84b); font-weight: 600; }

/* ───── from BINAURAL-TIMERS-MODULE-PRO/index.html ───── */
/* ===== Binaural Presets section ===== */
    #bp-presets-parent-container { margin-bottom: 1.5rem; }

    #bp-presets-parent-container .bp-tabs { display: flex; gap: 8px; margin-bottom: 1rem; }
    #bp-presets-parent-container .bp-tab.active { background: var(--color-accent); color: #111; border-color: var(--color-accent); }

    #bp-presets-parent-container .bp-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 12px; }
    @media (max-width: 900px) { #bp-presets-parent-container .bp-grid { grid-template-columns: repeat(2, 1fr); } }
    @media (max-width: 600px) { #bp-presets-parent-container .bp-grid { grid-template-columns: 1fr; } }

    #bp-presets-parent-container .bp-card {
        position: relative; display: flex; gap: 10px; min-height: 160px; cursor: pointer; overflow: hidden;
        background: var(--bg-dark-lighter); border: 1px solid var(--border-accent-light); border-radius: 4px;
        transition: border-color .2s ease, box-shadow .2s ease;
    }
    #bp-presets-parent-container .bp-card:hover { border-color: var(--color-accent); box-shadow: 0 0 8px var(--glow-accent); }
    #bp-presets-parent-container .bp-card-cover { width: 84px; flex-shrink: 0; object-fit: cover; background: #111; align-self: stretch; }
    #bp-presets-parent-container .bp-card-body { flex: 1; min-width: 0; padding: 10px 10px 10px 0; display: flex; flex-direction: column; gap: 4px; }
    #bp-presets-parent-container .bp-card-top { display: flex; align-items: flex-start; gap: 6px; }
    #bp-presets-parent-container .bp-card-title { flex: 1; font-size: 14px; font-weight: 600; color: var(--text-primary, #fff); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
    /* .bp-info-btn look comes from .bws-info-badge */
    #bp-presets-parent-container .bp-card-desc { font-size: 12px; color: var(--text-bright, rgba(255,255,255,0.8)); line-height: 1.4; display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; }
    #bp-presets-parent-container .bp-card-empty { border: 1px dashed var(--border-accent-light); border-radius: 4px; min-height: 160px; opacity: 0.22; }

    .bp-badge-free { background: rgba(75,200,100,0.25); }
    .bp-badge-basic { background: rgba(75,150,255,0.25); }
    .bp-badge-premium { background: rgba(180,75,255,0.25); }
    .bp-badge-paid { background: rgba(255,216,75,0.18); }
    .bp-badge-personal { background: rgba(255,255,255,0.14); }

    #bp-presets-parent-container .bp-pager { display: flex; align-items: center; justify-content: center; gap: 14px; margin-top: 1rem; }
    #bp-presets-parent-container .bp-pager button { padding: 6px 14px; border-radius: 6px; cursor: pointer; font-weight: 600; background: var(--bg-dark-lighter); border: 1px solid var(--border-accent-light); color: var(--color-accent); }
    #bp-presets-parent-container .bp-pager button:disabled { opacity: 0.3; cursor: not-allowed; }
    #bp-presets-parent-container .bp-pager-info { font-size: 13px; color: var(--text-muted); }
    #bp-presets-parent-container .bp-grid-msg { grid-column: 1 / -1; text-align: center; color: var(--text-muted); padding: 2rem 1rem; font-size: 14px; line-height: 1.6; }

    /* Preset info modal — box, cover and short-viewport clamp come from the
       cover-on-top info modal template (see .alb-modal). Only the paddings and
       the type are set here. */
    .bp-modal-body { padding: 14px 18px 4px; overflow-y: auto; }
    /* No max-height here: the modal has a height budget and the body scrolls, so
       a second scroller on the description would nest one inside the other. */
    .bp-modal-desc,
    .fp-modal-desc,
    .ip-modal-desc { font-size: 13px; line-height: 1.55; color: rgba(248,249,250,0.8); }
    .bp-modal-action { padding: 16px; flex-shrink: 0; }
    .bp-modal-load,
    .bp-modal-goto,
    .fp-modal-load,
    .fp-modal-goto,
    .ip-modal-load,
    .ip-modal-goto { width: 100%; padding: 11px; border-radius: 8px; cursor: pointer; font-size: 13px; font-weight: 700; border: 1px solid #d9b53c; background: #d9b53c; color: #000; transition: background-color .2s ease, border-color .2s ease; }
    .bp-modal-load:hover:not(:disabled), .bp-modal-goto:hover,
    .fp-modal-load:hover:not(:disabled), .fp-modal-goto:hover,
    .ip-modal-load:hover:not(:disabled), .ip-modal-goto:hover {
        background: var(--color-accent, #ffd84b); border-color: var(--color-accent, #ffd84b);
    }
    .bp-modal-load:disabled { opacity: 0.5; cursor: default; }
    /* Two side-by-side destinations after a preset loads (Timers | Media Mixer).
       Shared by the frequency, binaural and isochronic modals — the buttons are
       width:100% on their own, so the row is what divides the width. */
    .fp-modal-goto-row { display: flex; gap: 8px; }
    .fp-modal-goto-row > button { flex: 1 1 0; min-width: 0; }

    /* Admin (official publish) — delete button in the preset info modal */
    .bp-modal-del,
    .fp-modal-del,
    .ip-modal-del { width: 100%; margin-top: 8px; padding: 9px; border-radius: 8px; cursor: pointer; font-size: 12px; font-weight: 600; border: 1px solid var(--border-accent-light); background: var(--bg-dark-lighter); color: var(--color-accent); }
    .bp-modal-del:hover,
    .fp-modal-del:hover,
    .ip-modal-del:hover { background: #f44336; border-color: #f44336; color: #fff; }

    /* Admin (official publish) — note in the save dialog */
    .bp-save-official-note,
    .fp-save-official-note,
    .ip-save-official-note { font-size: 12px; line-height: 1.45; color: var(--color-accent); background: var(--bg-dark-light); border: 1px solid var(--border-accent-light); border-radius: 5px; padding: 8px 10px; margin: 0 0 12px; }

    /* Save dialog (reuses .bp-modal-overlay) */
    .bp-save-dialog,
    .fp-save-dialog,
    .ip-save-dialog { width: 100%; max-width: 380px; border: 1px solid var(--color-accent); border-radius: 6px; padding: 18px; box-shadow: 0 0 16px var(--glow-accent); }
    .bp-save-dialog h3,
    .fp-save-dialog h3,
    .ip-save-dialog h3 { margin: 0 0 14px; font-size: 15px; color: var(--color-accent); }
    .bp-save-dialog label,
    .fp-save-dialog label,
    .ip-save-dialog label { display: block; font-size: 12px; color: var(--text-muted); margin: 0 0 4px; }
    .bp-save-dialog input,
    .bp-save-dialog textarea,
    .fp-save-dialog input,
    .fp-save-dialog textarea,
    .ip-save-dialog input,
    .ip-save-dialog textarea { width: 100%; box-sizing: border-box; padding: 9px; margin-bottom: 12px; border-radius: 5px; border: 1px solid var(--border-accent-light); background: var(--bg-dark-light); color: #fff; font-size: 13px; font-family: inherit; }
    .bp-save-dialog textarea,
    .fp-save-dialog textarea,
    .ip-save-dialog textarea,
    .mr-save-dialog textarea { resize: vertical; min-height: 56px; }
    .bp-save-dialog-row,
    .fp-save-dialog-row,
    .ip-save-dialog-row { display: flex; gap: 8px; }
    /* Primary action, same two-tone as .fp-modal-load: darker yellow at rest,
       accent yellow under the cursor. It used to sit AT the accent with no hover
       state at all, so it read as already-hovered and never responded. */
    .bp-save-confirm,
    .fp-save-confirm,
    .ip-save-confirm { border: 1px solid #d9b53c; background: #d9b53c; color: #000; transition: background-color .2s ease, border-color .2s ease; }
    .bp-save-confirm:hover:not(:disabled),
    .fp-save-confirm:hover:not(:disabled),
    .ip-save-confirm:hover:not(:disabled) {
        background: var(--color-accent, #ffd84b); border-color: var(--color-accent, #ffd84b);
    }
    .bp-save-cancel,
    .fp-save-cancel,
    .ip-save-cancel { border: 1px solid var(--border-accent-light); background: var(--bg-dark-lighter); color: var(--text-muted); }

    /* Tags — card chips, filter bar (Personal), save-dialog entry */
    .mlib-rc-tags { display: flex; flex-wrap: wrap; gap: 5px; }
    .mr-video-opt { display: flex; flex-direction: column; gap: 4px; flex: 1; min-width: 180px; }
    .bp-save-dialog .bp-tags-field,
    .fp-save-dialog .fp-tags-field,
    .ip-save-dialog .ip-tags-field { display: flex; flex-wrap: wrap; align-items: center; gap: 6px; box-sizing: border-box; width: 100%; padding: 7px 8px; margin-bottom: 12px; border-radius: 5px; border: 1px solid var(--border-accent-light); background: var(--bg-dark-light); }
    .bp-save-dialog .bp-tags-input,
    .fp-save-dialog .fp-tags-input,
    .ip-save-dialog .ip-tags-input { flex: 1; min-width: 130px; width: auto; box-sizing: border-box; padding: 4px 2px; margin: 0; border: 0; background: transparent; color: #fff; font-size: 13px; font-family: inherit; }
    .bp-save-dialog .bp-tags-input:focus { outline: none; }
    .bp-tag-badge,
    .fp-tag-badge,
    .ip-tag-badge { display: inline-flex; align-items: center; gap: 5px; font-size: 10px; font-weight: 700; letter-spacing: 0.06em; text-transform: uppercase; padding: 0.18rem 0.2rem 0.18rem 0.55rem; border-radius: 20px; white-space: nowrap; background: rgba(255,216,75,0.12); border: 1px solid rgba(255,216,75,0.35); color: var(--color-accent); }
    .bp-tag-remove,
    .fp-tag-remove,
    .ip-tag-remove { display: inline-flex; align-items: center; justify-content: center; width: 15px; height: 15px; border-radius: 50%; border: 0; cursor: pointer; background: rgba(255,216,75,0.30); color: #111; font-size: 11px; line-height: 1; padding: 0; }
    .bp-tag-remove:hover { background: #f44336; color: #fff; }

/* ───── from BINAURAL-TIMERS-MODULE-PRO/index.html ───── */
/* ===== Frequency Timers — only layout the shared system doesn't cover.
       Fonts/colors come from the shared .bws-* classes + design tokens. ===== */

    /* ── Session position track: segmented bar (one rounded bar per timer) ── */
    #bt-main-parent-container .bt-seek-wrap,
    #ft-main-parent-container .ft-seek-wrap,
    #it-main-parent-container .it-seek-wrap { position: relative; flex: 3; min-width: 0; display: flex; align-items: center; }
    #bt-main-parent-container .bt-seek-wrap .hpp-seek,
    #ft-main-parent-container .ft-seek-wrap .hpp-seek,
    #it-main-parent-container .it-seek-wrap .hpp-seek { flex: 1; width: auto; position: relative; z-index: 1; background: transparent !important; }
    #bt-main-parent-container .bt-seek-segs { position: absolute; left: 0; right: 0; top: 50%; height: 6px; transform: translateY(-50%); display: flex; align-items: center; gap: 1px; pointer-events: none; }

    /* Timer cards */
    /* Gap / bottom margin leave room for the green add-timer pair, which hangs off
       each card's bottom border (see .bt-add-timer-actions in the shared rules). */
    /* margin-top leaves room for the lead insert bar, which hangs above the first card. */
    #bt-main-parent-container #bt-timers { display: flex; flex-direction: column; gap: 2.5rem; margin-top: 1.75rem; margin-bottom: 1.75rem; }
    #bt-main-parent-container .bt-timer {
        position: relative;
        background: var(--bg-dark-light);
        border: 1px solid var(--border-accent-light);
        border-radius: 4px;
        transition: border-color .2s ease, box-shadow .2s ease;
    }
    /* Two states, same pair as a playlist row. ACTIVE is the cued card — the one
       a press of play would run: solid accent border. RUNNING is that card with the
       transport moving: the border breathes and the card takes a faint golden fill.
       The fill is opaque on purpose — a translucent tint would show the page's
       background sweep through, so the first and last card would not match. */
    #bt-main-parent-container .bt-timer.bt-timer-active {
        border-color: var(--color-accent);
        box-shadow: 0 0 8px var(--glow-accent);
    }
    #bt-main-parent-container .bt-timer.bt-timer-running {
        background: #17140b;
        animation: bws-pl-glow 2.6s ease-in-out infinite;
    }
    /* ── Pause card: a silent gap between timers ──────────────────────────────
       The same container the playlist modules give a pause row — the chips and the
       custom H:M:S share their rules outright (see the pause block in the shared
       playlist section), so the two read as one control wherever it appears. It
       stays a .bt-timer so the green discs still hang off its bottom edge; the card
       look is restated here because .bt-timer is scoped to this container and would
       otherwise win. */
    #bt-main-parent-container .bt-timer--pause:not(.bt-timer-running) {
        background: #121212;
    }
    /* The green discs hang 13px below the card's bottom border, so the content
       keeps ~1.2rem of clearance underneath and never sits behind them. */
    #bt-main-parent-container .bt-pause-top {
        display: flex; align-items: center; gap: 0.5rem; padding: 0.6rem 0.6rem 1.25rem; flex-wrap: wrap;
    }
    /* Accent disc with a black glyph — the same 20px badge the numbered timer
       cards carry, so a pause reads as a peer of the timers around it. */
    #bt-main-parent-container .bt-pause-glyph {
        flex-shrink: 0; width: 20px; height: 20px; display: flex; align-items: center; justify-content: center;
        border-radius: 50%; background: var(--color-accent, #ffd84b); color: #000;
    }
    #bt-main-parent-container .bt-pause-title { flex: 0 0 auto; font-size: 13px; color: var(--color-accent); }
    /* The first card also carries the lead insert bar on its top border, so a
       pause in that slot needs the same clearance above as it has below. */
    #bt-main-parent-container #bt-timers > .bt-timer--pause:first-child .bt-pause-top { padding-top: 1.25rem; }
    #bt-main-parent-container .bt-pause-top .bt-remove { margin-left: auto; }
    #bt-main-parent-container .bt-timer-head {
        display: flex; align-items: center; justify-content: space-between; gap: 0.75rem;
        padding: 0.75rem; border-bottom: 1px solid var(--border-accent-light);
    }
    /* Fixed-size: the volume control is the only thing that may resize, so the title
       and its H:M:S boxes must never be shrunk out from under the preview button. */
    #bt-main-parent-container .bt-timer-head-left { display: flex; align-items: center; gap: 0.75rem; flex: 0 0 auto; min-width: 0; }
    /* .bt-timer-name — shared style defined in CSS/style.css */
    /* Duration boxes reuse the pause cards' .bt-pause-hms / .bt-pause-colon styling
       (incl. its light-theme rules) — only the row's own gap is set here. */
    #bt-main-parent-container .bt-duration { display: flex; align-items: center; gap: 0.25rem; }
    /* Header duration quick-picks — the only chips left (the pause cards' quick-picks
       are gone; a gap's length is typed in its H:M:S boxes). Natural width on a
       single line: the pills and the ✕ are the header's fixed right-hand end, and the
       volume control beside them is what takes up the slack. */
    #bt-main-parent-container .bt-dur-chips { display: flex; align-items: center; gap: 0.3rem; flex: 0 0 auto; flex-wrap: nowrap; }

    /* Timer remove — grey circle with a centered dark X (SVG), red with white X on hover */
    #bt-main-parent-container .bt-remove {
        width: 26px; height: 26px; flex-shrink: 0; border-radius: 50%; border: 0; cursor: pointer; padding: 0;
        background: rgba(211,211,211,1) url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' width='24' height='24'%3E%3Cpath d='M18 6L6 18M6 6l12 12' stroke='%23141414' stroke-width='2.5' stroke-linecap='round'/%3E%3C/svg%3E") center/13px no-repeat;
        transition: all .2s ease;
    }
    #bt-main-parent-container .bt-remove:hover:not(:disabled) {
        background: var(--color-danger) url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' width='24' height='24'%3E%3Cpath d='M18 6L6 18M6 6l12 12' stroke='%23FFFFFF' stroke-width='2.5' stroke-linecap='round'/%3E%3C/svg%3E") center/13px no-repeat;
    }
    #bt-main-parent-container .bt-remove:disabled { opacity: 0.3; cursor: not-allowed; }

    /* Row remove — grey circle with a centered dark X (SVG, so it's geometrically
       centered at this small size), red with white X on hover. Matches .bt-remove. */
    #bt-main-parent-container .bt-row-remove {
        width: 20px; height: 20px; flex-shrink: 0; border-radius: 50%; border: 0; cursor: pointer; padding: 0;
        background: rgba(211,211,211,1) url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' width='24' height='24'%3E%3Cpath d='M18 6L6 18M6 6l12 12' stroke='%23141414' stroke-width='2.5' stroke-linecap='round'/%3E%3C/svg%3E") center/10px no-repeat;
        transition: all .2s ease;
    }
    #bt-main-parent-container .bt-row-remove:hover:not(:disabled) {
        background: var(--color-danger) url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' width='24' height='24'%3E%3Cpath d='M18 6L6 18M6 6l12 12' stroke='%23FFFFFF' stroke-width='2.5' stroke-linecap='round'/%3E%3C/svg%3E") center/10px no-repeat;
    }
    #bt-main-parent-container .bt-row-remove:disabled { opacity: 0.3; cursor: not-allowed; }

    /* Timer volume — sits in the head row between the time inputs and close button */
    /* The header's one elastic element — it grows to close the gap between the preview
       button and the pills. The basis must be 0, not auto: the shared
       .bws-volume-control sets width:100%, so an auto basis asks for the whole header
       and the resulting shrink is taken out of the title group instead. border-box
       because .fsb-row-volume-control is content-box with .75rem side padding, which
       otherwise spills 24px past whatever width flex resolves for it. */
    #bt-main-parent-container .bt-timer-head-vol { box-sizing: border-box; flex: 1 1 0%; min-width: 0; margin-top: 0; }

    /* Timer preview — small round accent play button in front of the volume slider.
       Holds both glyphs; .bt-preview-on swaps play for stop. */
    #bt-main-parent-container .bt-preview {
        width: 26px; height: 26px; flex-shrink: 0; padding: 0; border: 0; border-radius: 50%;
        display: flex; align-items: center; justify-content: center; cursor: pointer;
        background: #d9b53c; color: #111;
        transition: background-color .2s ease;
    }
    #bt-main-parent-container .bt-preview:hover:not(:disabled) { background: var(--color-accent, #ffd84b); }
    #bt-main-parent-container .bt-preview:disabled { opacity: 0.3; cursor: not-allowed; }
    #bt-main-parent-container .bt-preview .bt-preview-play { margin-left: 1px; }   /* optically centre the triangle */
    #bt-main-parent-container .bt-preview .bt-preview-stop { display: none; }
    /* Playing: red with the stop square, no ring — the colour change is the whole
       signal, matching the motionless hover everywhere else. Same darker-at-rest /
       lighter-on-hover pair as the yellow state, in red. */
    #bt-main-parent-container .bt-preview.bt-preview-on { background: #d32f2f; color: #fff; }
    #bt-main-parent-container .bt-preview.bt-preview-on:hover:not(:disabled) { background: #f44336; }
    #bt-main-parent-container .bt-preview.bt-preview-on .bt-preview-play { display: none; }
    #bt-main-parent-container .bt-preview.bt-preview-on .bt-preview-stop { display: block; }

    /* Binaural pair rows */
    /* The yellow add/duplicate pair sits centred on each row card's bottom border;
       the gap and bottom padding are what keep it clear of the next row and of the
       green add-timer pair on the card's own bottom border. */
    #bt-main-parent-container .bt-rows { padding: 0.75rem 0.75rem 3.25rem; display: flex; flex-direction: column; gap: 1.75rem; }
    /* Padding is even on all four sides so the carrier sub-row's volume cell runs
       the full width up to the card's inner right edge. The X needs no reserved
       gutter: it is absolutely placed at the top-right, over the beat sub-row,
       whose fn / wave / volume columns are empty. */
       #bt-main-parent-container .bt-row {
        position: relative;
        border: 1px solid var(--border-accent-light); border-radius: 6px;
        /* The yellow add/duplicate pair straddles this card's bottom border and
           reaches 13px above it, so the bottom padding has to clear that disc —
           same 20px Frequency Timers gives its rows. */
        padding: 0.6rem 0.6rem 20px; background: rgba(255,255,255,0.02);
    }
    /* Badge (row number) sits to the left of both preset dropdowns, vertically centered */
    #bt-main-parent-container .bt-row-main { display: flex; align-items: center; gap: 0.6rem; }
    #bt-main-parent-container .bt-badge {
        width: 26px; height: 26px; border-radius: 4px; background: var(--color-accent);
        color: #111; font-weight: 600; font-size: 12px; display: flex; align-items: center; justify-content: center; flex-shrink: 0;
    }
    #bt-main-parent-container .bt-subrows { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 0.5rem; }

    /* Both sub-rows share one column template so preset + custom align between
       beat and carrier. Carrier fills all columns; beat leaves fn/wave/vol empty. */
    /* The wave column is the gear button's own 34px, not the 152px the old
       four-toggle group needed, so the volume cell starts right after the gear
       instead of across a dead gap. It stays a fixed track (not auto) because
       each sub-row is its own grid: an auto column would collapse to 0 on the
       beat row, where the wave cell is absent, and pull its fr columns out of
       line with the carrier row's. */
    /* preset · custom Hz · function · wave gear · row preview · volume */
    #bt-main-parent-container .bt-subrow {
        display: grid;
        grid-template-columns: minmax(120px, 1.15fr) minmax(70px, 0.55fr) minmax(96px, 0.62fr) 34px 26px minmax(120px, 1.4fr);
        gap: 0.5rem; align-items: center;
    }
    #bt-main-parent-container .bt-row .bbp-frequency-input { width: 100%; }
    #bt-main-parent-container .bt-row .bws-dropdown { width: 100%; }
    #bt-main-parent-container .bt-subrow .bws-volume-control { flex: 1; }
    /* Row volume cell — the shared .fsb-row-volume-control is written for a wrapped,
       full-width row: it carries a top margin and a content-box width: 100% whose
       .75rem side padding then spills 24px past its grid track. Inside this grid it
       has to fill its track exactly and centre with the rest of the carrier sub-row.
       (The X sits absolutely at the card's top-right, so nothing collides here.) */
    #bt-main-parent-container .bt-subrow .bt-vol-cell {
        box-sizing: border-box;
        margin-top: 0;
        flex-basis: auto;
    }

    /* "No Sound" beat, or beat not yet set: dim + disable the carrier sub-row (beat row stays active). */
    #bt-main-parent-container .bt-row-nosound .bt-subrow-carrier,
    #bt-main-parent-container .bt-row-beatless .bt-subrow-carrier,
    #bt-main-parent-container .bt-subrow-carrier.bt-carrier-nosound .bt-fn-cell,
    #bt-main-parent-container .bt-subrow-carrier.bt-carrier-nosound .bt-wave-cell,
    #bt-main-parent-container .bt-subrow-carrier.bt-carrier-nosound .bt-vol-cell { opacity: 0.35; pointer-events: none; }

    /* Carrier not yet populated: dim fn / waveform / volume in the carrier sub-row. */

    /* Remove button — top-right corner of the row card */
    #bt-main-parent-container .bt-row > .bt-row-remove { position: absolute; top: 6px; right: 6px; }

    /* Manual Hz field — match dropdown height; gold left-aligned "Custom..." placeholder */
    #bt-main-parent-container .bt-hz-input {
        height: 38px; text-align: left; padding: 0.625rem 0.5rem; font-size: 13px; font-variant-numeric: tabular-nums;
    }
    #bt-main-parent-container .bt-hz-input::placeholder { color: var(--color-accent); opacity: 0.85; }
    #bt-main-parent-container .bt-hz-input::-moz-placeholder { color: var(--color-accent); opacity: 0.85; }

    /* Preset trigger — looks like .bws-dropdown but holds a left-aligned label + caret */
    #bt-main-parent-container .bt-preset-trigger {
        display: flex; align-items: center; text-align: left; cursor: pointer;
        color: var(--text-muted);
    }
    #bt-main-parent-container .bt-preset-trigger.has-value { color: var(--color-accent); }
    #bt-main-parent-container .bt-preset-trigger .bt-preset-trigger-text {
        flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
    }

    /* Preset dropdown popup: styled by the shared block near .hpp-preset-dropdown. */
    /* Field-level preset info button (to the right of the beat / carrier trigger) */
    #bt-main-parent-container .bt-preset-cell { display: flex; align-items: center; gap: 0.4rem; }
    #bt-main-parent-container .bt-preset-cell .bt-preset-trigger { flex: 1; min-width: 0; }
    /* look + disabled state come from .bws-info-badge */

    /* ── Preset info modal (mirrors mobile FrequencyInfoModal) ── */
    .bt-info-overlay,
    .ft-info-overlay,
    .it-info-overlay,
    .tin-info-overlay {
        position: fixed; inset: 0; z-index: 5000;
        display: flex; align-items: center; justify-content: center;
        gap: 14px; padding: 24px; overflow: hidden;
        font-family: 'Roboto',-apple-system,BlinkMacSystemFont,"Segoe UI","Helvetica Neue",sans-serif;
    }
    .bt-info-modal,
    .ft-info-modal,
    .it-info-modal {
        width: 100%; max-width: 720px; min-width: 0; max-height: calc(100vh - 100px); display: flex; flex-direction: column;
        border: 1px solid var(--color-accent); border-radius: 8px;
        box-shadow: 0 0 10px var(--glow-accent), 0 8px 24px rgba(0,0,0,0.6); overflow: hidden;
    }
    .bt-info-modal .bt-im-head,
    .ft-info-modal .ft-im-head,
    .it-info-modal .it-im-head {
        display: flex; align-items: center; gap: 0.6rem; padding: 1rem 1rem 0.75rem;
    }
    /* .bt-im-badge / .ft-im-badge / .it-im-badge look comes from .bws-info-badge */
    .bt-info-modal .bt-im-title { flex: 1; color: var(--text-primary, #fff); font-size: 16px; font-weight: 700; }
    .bt-info-modal .bt-im-close,
    .sbp-ppm-close,
    .sbp-bim-close {
        width: 28px; height: 28px; flex-shrink: 0; border-radius: 50%; border: 0; cursor: pointer;
        background: rgba(211,211,211,1); color: rgba(20,20,20,0.95); font-size: 12px; line-height: 1;
        display: flex; align-items: center; justify-content: center;
    }
    .bt-info-modal .bt-im-close:hover { background: var(--color-danger); color: #fff; }
    .bt-info-modal .bt-im-underline { height: 1px; background: rgba(128,128,128,0.5); margin: 0 1rem; }
    .bt-info-modal .bt-im-body,
    .ft-info-modal .ft-im-body,
    .it-info-modal .it-im-body { overflow-y: auto; padding: 1rem; }
    .bt-info-modal .bt-im-image { width: 100%; aspect-ratio: 1 / 1; object-fit: contain; border-radius: 6px; display: block; background: rgba(255,255,255,0.03); }
    .bt-info-modal .bt-im-top { display: grid; grid-template-columns: 260px 1fr; gap: 16px; margin-bottom: 0; align-items: start; }
    .bt-info-modal .bt-im-top.no-image { grid-template-columns: 1fr; }
    .bt-info-modal .bt-im-main { min-width: 0; }
    /* Lower section: left col = Play Preview + Details + Usage; right col = Therapeutic + Safety */
    .bt-info-modal .bt-im-lower {
        margin-top: 14px; padding-top: 14px; border-top: 1px solid rgba(255,216,75,0.12);
        display: grid; grid-template-columns: 1fr 1fr; gap: 10px; align-items: start;
    }
    .bt-info-modal .bt-im-col { display: flex; flex-direction: column; gap: 10px; min-width: 0; }
    .bt-info-modal .bt-im-col .bt-im-details,
    .bt-info-modal .bt-im-col .bt-im-section,
    .bt-info-modal .bt-im-col .bt-im-preview-row { margin: 0; }
    .bt-info-modal .bt-im-preview-btn { display: flex; width: 100%; align-items: center; justify-content: center; gap: 0.4rem; }
    .bt-info-modal .bt-im-preview-btn.bt-preview-on { background: var(--color-danger, #e0564a); border-color: var(--color-danger, #e0564a); color: #fff; }
    .bt-info-modal .bt-im-set-btn,
    .sbp-bim-set-btn { width: 100%; margin-top: 16px; font-size: 14px; font-weight: 500; }
    @media (max-width: 600px) {
        .bt-info-modal .bt-im-top { grid-template-columns: 1fr; }
        .bt-info-modal .bt-im-lower { grid-template-columns: 1fr; }
        .bt-info-overlay,
        .ft-info-overlay,
        .it-info-overlay { gap: 6px; padding: 10px; }
    }
    .bt-info-modal .bt-im-hz { color: var(--color-accent); font-size: 22px; font-weight: 700; margin-bottom: 2px; }
    .bt-info-modal .bt-im-subtitle { color: rgba(248,249,250,0.9); font-size: 14px; font-weight: 500; margin-bottom: 12px; }
    .bt-info-modal .bt-im-desc,
    .sbp-bim-desc { color: rgba(248,249,250,0.85); font-size: 13px; line-height: 20px; margin-bottom: 14px; }
    .bt-info-modal .bt-im-details {
        margin-bottom: 10px; padding: 0.5rem 0.625rem; border-radius: 4px;
        background: rgba(255,216,75,0.05); border: 1px solid rgba(255,216,75,0.12);
    }
    .bt-info-modal .bt-im-detail,
    .sbp-bim-detail { color: rgba(248,249,250,0.8); font-size: 12px; line-height: 20px; }
    .bt-info-modal .bt-im-detail-label { color: var(--color-accent); font-weight: 600; }
    .bt-info-modal .bt-im-section,
    .sbp-bim-section {
        margin-top: 10px; padding: 0.625rem; border-radius: 4px; background: rgba(255,255,255,0.03);
    }
    .bt-info-modal .bt-im-section-title {
        color: var(--color-accent); font-size: 13px; font-weight: 600; margin-bottom: 8px;
        padding-bottom: 4px; border-bottom: 1px solid rgba(255,216,75,0.2);
    }
    .bt-info-modal .bt-im-section-item { color: rgba(248,249,250,0.8); font-size: 12px; line-height: 18px; margin-bottom: 4px; }

    /* Waveform toggle buttons (4 in a row, same look as Frequency Session Builder) */
    #bt-main-parent-container .bt-wave-toggles { display: flex; gap: 0.3rem; }
    #bt-main-parent-container .bt-wave-toggles .fsb-row-wave-button { width: 34px; height: 34px; }

    @media (max-width: 760px) {
        #bt-main-parent-container .bt-subrow-beat {
            grid-template-columns: 1fr 1fr;
            grid-template-areas: "sel sel" "hz hz";
            row-gap: 0.4rem;
        }
        /* The gear and the row-preview button are content-sized; the volume box takes
           the rest of the last line. */
        #bt-main-parent-container .bt-subrow-carrier {
            grid-template-columns: auto auto 1fr;
            grid-template-areas: "sel sel sel" "hz hz fn" "wave prev vol";
            row-gap: 0.4rem;
        }
        #bt-main-parent-container .bt-subrow .bt-preset-cell { grid-area: sel; }
        #bt-main-parent-container .bt-subrow .bt-hz-cell { grid-area: hz; }
        #bt-main-parent-container .bt-subrow .bt-fn-cell { grid-area: fn; }
        #bt-main-parent-container .bt-subrow .bt-wave-cell { grid-area: wave; justify-self: start; }
        #bt-main-parent-container .bt-subrow .bt-row-preview { grid-area: prev; justify-self: start; }
        #bt-main-parent-container .bt-subrow .bt-vol-cell { grid-area: vol; }
    }

/* ───── from FREQUENCY-TIMERS-MODULE-PRO/index.html ───── */
/* ===== Frequency Presets section ===== */
    #fp-presets-parent-container { margin-bottom: 1.5rem; }

    #fp-presets-parent-container .fp-tabs { display: flex; gap: 8px; margin-bottom: 1rem; }
    #fp-presets-parent-container .fp-tab.active { background: var(--color-accent); color: #111; border-color: var(--color-accent); }

    #fp-presets-parent-container .fp-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 12px; }
    @media (max-width: 900px) { #fp-presets-parent-container .fp-grid { grid-template-columns: repeat(2, 1fr); } }
    @media (max-width: 600px) { #fp-presets-parent-container .fp-grid { grid-template-columns: 1fr; } }

    #fp-presets-parent-container .fp-card {
        position: relative; display: flex; gap: 10px; min-height: 160px; cursor: pointer; overflow: hidden;
        background: var(--bg-dark-lighter); border: 1px solid var(--border-accent-light); border-radius: 4px;
        transition: border-color .2s ease, box-shadow .2s ease;
    }
    #fp-presets-parent-container .fp-card:hover { border-color: var(--color-accent); box-shadow: 0 0 8px var(--glow-accent); }
    #fp-presets-parent-container .fp-card-cover { width: 84px; flex-shrink: 0; object-fit: cover; background: #111; align-self: stretch; }
    #fp-presets-parent-container .fp-card-body { flex: 1; min-width: 0; padding: 10px 10px 10px 0; display: flex; flex-direction: column; gap: 4px; }
    #fp-presets-parent-container .fp-card-top { display: flex; align-items: flex-start; gap: 6px; }
    #fp-presets-parent-container .fp-card-title { flex: 1; font-size: 14px; font-weight: 600; color: var(--text-primary, #fff); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
    /* .fp-info-btn look comes from .bws-info-badge */
    #fp-presets-parent-container .fp-card-desc { font-size: 12px; color: var(--text-bright, rgba(255,255,255,0.8)); line-height: 1.4; display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; }
    #fp-presets-parent-container .fp-card-empty { border: 1px dashed var(--border-accent-light); border-radius: 4px; min-height: 160px; opacity: 0.22; }

    /* Entry cards — the AI builder / save-current-setup tiles in the frequency,
       binaural and isochronic preset grids. Same card shell as a real preset
       (drawn cover, no tier badge), with the yellow action button pinned to the
       bottom of the body. Their gold-to-accent hover lives in "Entry cards". */
    /* ── Shared card tag chip — single source of truth for every preset/album card ──
       Twelve modules render the same pill; module rules must not restate it. */
    .bws-card-tags { display: flex; flex-wrap: wrap; gap: 5px; margin-top: 2px; }
    .bws-card-tag,
    .bws-modal-tag {
        font-size: 9px; font-weight: 400; letter-spacing: 0.06em; text-transform: uppercase;
        padding: 0.12rem 0.45rem; border-radius: 20px; white-space: nowrap;
        background: rgba(255,216,75,0.10); border: 1px solid rgba(255,216,75,0.3);
        color: var(--color-accent, #ffd84b);
    }

    .bws-card-create-btn {
        margin-top: auto; align-self: stretch; width: 100%; padding: 10px 14px; border-radius: 5px; cursor: pointer;
        font-size: 14px; font-weight: 700; font-family: inherit;
        border: 1px solid var(--color-accent); background: var(--color-accent); color: #000;
    }

    .fp-badge-free { background: rgba(75,200,100,0.25); }
    .fp-badge-basic { background: rgba(75,150,255,0.25); }
    .fp-badge-premium { background: rgba(180,75,255,0.25); }
    .fp-badge-paid { background: rgba(255,216,75,0.18); }
    /* Personal tab: own presets have no access tier, so the slot the tier badge
       occupies on Brainwavesounds cards says PERSONAL instead. */
    .fp-badge-personal { background: rgba(255,255,255,0.14); }

    #fp-presets-parent-container .fp-pager { display: flex; align-items: center; justify-content: center; gap: 14px; margin-top: 1rem; }
    #fp-presets-parent-container .fp-pager button { padding: 6px 14px; border-radius: 6px; cursor: pointer; font-weight: 600; background: var(--bg-dark-lighter); border: 1px solid var(--border-accent-light); color: var(--color-accent); }
    #fp-presets-parent-container .fp-pager button:disabled { opacity: 0.3; cursor: not-allowed; }
    #fp-presets-parent-container .fp-pager-info { font-size: 13px; color: var(--text-muted); }
    #fp-presets-parent-container .fp-grid-msg { grid-column: 1 / -1; text-align: center; color: var(--text-muted); padding: 2rem 1rem; font-size: 14px; line-height: 1.6; }

    /* Preset info modal */
    .fp-modal-body { padding: 14px 18px 4px; overflow-y: auto; }
    .fp-modal-action { padding: 16px; flex-shrink: 0; }
    .fp-modal-load:disabled { opacity: 0.5; cursor: default; }

    /* Save dialog (reuses .fp-modal-overlay) */

    /* Presets-builder modal, stage 1 (describe + Quick/Assisted + chat). Wider
       than the save dialog it later morphs into, since the chat log lives here.
       Shared by the frequency, binaural and isochronic builders. */
    /* max-height matches .bws-cover-dialog — the builder morphs into that dialog
       mid-flow, so the two must stop at the same place or the box jumps height
       when it does. See the note there for the 100px. */
    .bws-build-dialog {
        position: relative; width: 100%; max-width: 560px; max-height: calc(100vh - 100px); overflow-y: auto;
        border: 1px solid var(--color-accent); border-radius: 6px;
        padding: 18px; box-shadow: 0 0 16px var(--glow-accent);
    }
    /* ── Frosted-glass modals ─────────────────────────────────────────────
       Every modal in the app shares one look,
first drawn for the media library's
       "Enhance with AI" dialog: the page behind is blurred and dimmed by the
       overlay,
       and the panel on top is translucent rather than near-opaque,
so
       that blur reads through it as glass.
       These two rules are the only place the colours and blur radii live. A
       module's own overlay/panel rules carry layout,
borders and shadows only —
       adding a modal means adding its two class names here,
nothing else. */
       .alb-modal-overlay,
       .alv-info-overlay,
       .bp-modal-overlay,
       .bt-info-overlay,
       .bws-playblock-overlay,
       .bws-sim-overlay,
       .dcp-modal-overlay,
       .fp-modal-overlay,
       .ft-info-overlay,
       .gcp-modal-overlay,
       .hcp-modal-overlay,
       .hpp-bim-overlay,
       .ip-modal-overlay,
       .it-info-overlay,
       .mlib-ai-modal-overlay,
       .mr-modal-overlay,
       .sbp-bowl-info-overlay,
       .sbp-ppm-overlay,
       .scp-modal-overlay,
       .sdp-bim-overlay,
       .sgp-bim-overlay,
       .tin-info-overlay {
        background: var(--glass-scrim);
        backdrop-filter: blur(2px);
        -webkit-backdrop-filter: blur(2px);
    }
    /* Carousel nav — the yellow circle chevrons flanking every info modal
       (handpan, singing bowls, gong/drum settings, timer presets). One rule; the
       modals differ only in what they carousel through. */
    .bt-im-nav,
    .bws-sim-nav,
    .ft-im-nav,
    .hpp-bim-nav,
    .it-im-nav,
    .sbp-bim-nav,
    .sbp-ppm-nav,
    .sdp-bim-nav,
    .sgp-bim-nav {
        flex-shrink: 0; width: 48px; height: 48px; border-radius: 50%; border: 0; cursor: pointer;
        background: var(--color-accent); color: #000;
        display: flex; align-items: center; justify-content: center;
        box-shadow: 0 4px 14px rgba(0,0,0,0.45);
        transition: background 0.15s ease, transform 0.15s ease;
    }
    .bt-im-nav:hover,
    .bws-sim-nav:hover,
    .ft-im-nav:hover,
    .hpp-bim-nav:hover,
    .it-im-nav:hover,
    .sbp-bim-nav:hover,
    .sbp-ppm-nav:hover,
    .sdp-bim-nav:hover,
    .sgp-bim-nav:hover { background: #fff; transform: scale(1.08); }
    .bt-im-nav:active,
    .bws-sim-nav:active,
    .ft-im-nav:active,
    .hpp-bim-nav:active,
    .it-im-nav:active,
    .sbp-bim-nav:active,
    .sbp-ppm-nav:active,
    .sdp-bim-nav:active,
    .sgp-bim-nav:active { transform: scale(0.95); }
    /* All seven shrink to 40px on narrow screens, but at the breakpoint their own
       modal reflows at — 600px for the timer preset modals, 560px for the rest —
       so the nav never shrinks out of step with the padding beside it. */
    @media (max-width: 600px) {
        .bt-im-nav,
        .ft-im-nav,
        .it-im-nav { width: 40px; height: 40px; }
    }
    @media (max-width: 560px) {
        .bws-sim-nav,
        .hpp-bim-nav,
        .sbp-bim-nav,
        .sbp-ppm-nav,
        .sdp-bim-nav,
        .sgp-bim-nav { width: 40px; height: 40px; }
    }

    /* Only the panel itself is translucent — its children (fields,
    chat log,
track
    rows,
    buttons) keep their own backgrounds,
so text stays readable. */
    .alb-modal,
    .alv-info-modal,
    .av2p-dialog,
    .bp-modal,
    .bp-save-dialog,
    .bt-info-modal,
    .bws-build-dialog,
    .bws-playblock-modal,
    .bws-sim-modal,
    .dcp-save-dialog,
    .fp-modal,
    .fp-save-dialog,
    .ft-info-modal,
    .gcp-save-dialog,
    .hcp-save-dialog,
    .hpp-bim-modal,
    .ip-modal,
    .ip-save-dialog,
    .it-info-modal,
    .mlib-enhance-panel,
    .mr-modal,
    .mr-save-dialog,
    .sbp-bowl-info-modal,
    .sbp-ppm-modal,
    .scp-save-dialog,
    .sdp-bim-modal,
    .sgp-bim-modal,
    .tin-info-modal {
        background: var(--glass-panel);
        backdrop-filter: blur(16px) saturate(1.15);
        -webkit-backdrop-filter: blur(16px) saturate(1.15);
    }

    /* Builder dialogs (pack, frequency, binaural, isochronic) title themselves
       like a preset's info modal (.fp-modal-name) — white, not accent yellow. */
    .bws-build-dialog h3 { margin: 0 0 14px; font-size: 16px; font-weight: 700; color: #fff; line-height: 1.35; }
    .bws-build-close {
        position: absolute; top: 10px; right: 10px; width: 26px; height: 26px; padding: 0;
        display: flex; align-items: center; justify-content: center; border-radius: 50%; cursor: pointer;
        border: 1px solid var(--border-accent-light); background: var(--bg-dark-lighter);
        color: var(--color-accent); font-size: 13px; line-height: 1;
    }
    .bws-build-close:hover { background: var(--color-accent); color: #000; }

    /* Tags — card chips, filter bar (Personal), save-dialog entry */
    /* Card + modal tag chips are clickable — one click filters the grid to that
       tag. Same gesture in the album, timer-preset and pack grids. */
    .alb-card-tag, .plp-pcard-tag,
    .fp-card-tag, .bp-card-tag, .ip-card-tag, .mlib-rc-tag,
    .fp-modal-tag, .bp-modal-tag, .ip-modal-tag,
    .prb-modal-tag { cursor: pointer; transition: background .15s ease, border-color .15s ease; }
    .alb-card-tag:hover, .plp-pcard-tag:hover,
    .fp-card-tag:hover, .bp-card-tag:hover, .ip-card-tag:hover, .mlib-rc-tag:hover,
    .fp-modal-tag:hover, .bp-modal-tag:hover, .ip-modal-tag:hover,
    .prb-modal-tag:hover { background: rgba(255,216,75,0.22); border-color: var(--color-accent, #ffd84b); }
    .fp-save-dialog .fp-tags-input:focus { outline: none; }
    .fp-tag-remove:hover { background: #f44336; color: #fff; }

/* ───── from FREQUENCY-TIMERS-MODULE-PRO/index.html ───── */
/* ===== Frequency Timers — only layout the shared system doesn't cover.
       Fonts/colors come from the shared .bws-* classes + design tokens. ===== */

    /* ── Session position track: segmented bar (one rounded bar per timer) ── */
    #ft-main-parent-container .ft-seek-segs { position: absolute; left: 0; right: 0; top: 50%; height: 6px; transform: translateY(-50%); display: flex; align-items: center; gap: 1px; pointer-events: none; }

    /* Timer cards */
    /* Gap / bottom margin leave room for the green add-timer pair, which hangs off
       each card's bottom border (see .ft-add-timer-actions in the shared rules). */
    /* margin-top leaves room for the lead insert bar, which hangs above the first card. */
    #ft-main-parent-container #ft-timers { display: flex; flex-direction: column; gap: 2.5rem; margin-top: 1.75rem; margin-bottom: 1.75rem; }
    #ft-main-parent-container .ft-timer {
        position: relative;
        background: var(--bg-dark-light);
        border: 1px solid var(--border-accent-light);
        border-radius: 4px;
        transition: border-color .2s ease, box-shadow .2s ease;
    }
    /* Two states, same pair as a playlist row. ACTIVE is the cued card — the one
       a press of play would run: solid accent border. RUNNING is that card with the
       transport moving: the border breathes and the card takes a faint golden fill.
       The fill is opaque on purpose — a translucent tint would show the page's
       background sweep through, so the first and last card would not match. */
    #ft-main-parent-container .ft-timer.ft-timer-active {
        border-color: var(--color-accent);
        box-shadow: 0 0 8px var(--glow-accent);
    }
    #ft-main-parent-container .ft-timer.ft-timer-running {
        background: #17140b;
        animation: bws-pl-glow 2.6s ease-in-out infinite;
    }
    /* ── Pause card: a silent gap between timers ──────────────────────────────
       The same container the playlist modules give a pause row — the chips and the
       custom H:M:S share their rules outright (see the pause block in the shared
       playlist section), so the two read as one control wherever it appears. It
       stays a .ft-timer so the green add/duplicate/pause trio still hangs off its
       bottom edge; the card look is restated here because .ft-timer is scoped to
       this container and would otherwise win. */
    #ft-main-parent-container .ft-timer--pause:not(.ft-timer-running) {
        background: #121212;
    }
    /* The green discs hang 13px below the card's bottom border, so the content
       keeps ~1.2rem of clearance underneath and never sits behind them. */
    #ft-main-parent-container .ft-pause-top {
        display: flex; align-items: center; gap: 0.5rem; padding: 0.6rem 0.6rem 1.25rem; flex-wrap: wrap;
    }
    /* Accent disc with a black glyph — the same 20px badge the numbered timer
       cards carry, so a pause reads as a peer of the timers around it. */
    #ft-main-parent-container .ft-pause-glyph {
        flex-shrink: 0; width: 20px; height: 20px; display: flex; align-items: center; justify-content: center;
        border-radius: 50%; background: var(--color-accent, #ffd84b); color: #000;
    }
    #ft-main-parent-container .ft-pause-title { flex: 0 0 auto; font-size: 13px; color: var(--color-accent); }
    /* The first card also carries the lead insert bar on its top border, so a
       pause in that slot needs the same clearance above as it has below. */
    #ft-main-parent-container #ft-timers > .ft-timer--pause:first-child .ft-pause-top { padding-top: 1.25rem; }
    #ft-main-parent-container .ft-pause-top .ft-remove { margin-left: auto; }
    #ft-main-parent-container .ft-timer-head {
        display: flex; align-items: center; justify-content: space-between; gap: 0.75rem;
        padding: 0.75rem; border-bottom: 1px solid var(--border-accent-light);
    }
    /* Fixed-size: the volume control is the only thing that may resize, so the title
       and its H:M:S boxes must never be shrunk out from under the preview button. */
    #ft-main-parent-container .ft-timer-head-left { display: flex; align-items: center; gap: 0.75rem; flex: 0 0 auto; min-width: 0; }
    /* .ft-timer-name — shared style defined in CSS/style.css */
    /* Duration boxes reuse the pause cards' .ft-pause-hms / .ft-pause-colon styling
       (incl. its light-theme rules) — only the row's own gap is set here. */
    #ft-main-parent-container .ft-duration { display: flex; align-items: center; gap: 0.25rem; }
    /* Header duration quick-picks — the only chips left (the pause cards' quick-picks
       are gone; a gap's length is typed in its H:M:S boxes). Natural width on a
       single line: the pills and the ✕ are the header's fixed right-hand end, and the
       volume control beside them is what takes up the slack. */
    #ft-main-parent-container .ft-dur-chips { display: flex; align-items: center; gap: 0.3rem; flex: 0 0 auto; flex-wrap: nowrap; }

    /* Timer remove — grey circle with a centered dark X (SVG), red with white X on hover */
    #ft-main-parent-container .ft-remove {
        width: 26px; height: 26px; flex-shrink: 0; border-radius: 50%; border: 0; cursor: pointer; padding: 0;
        background: rgba(211,211,211,1) url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' width='24' height='24'%3E%3Cpath d='M18 6L6 18M6 6l12 12' stroke='%23141414' stroke-width='2.5' stroke-linecap='round'/%3E%3C/svg%3E") center/13px no-repeat;
        transition: all .2s ease;
    }
    #ft-main-parent-container .ft-remove:hover:not(:disabled) {
        background: var(--color-danger) url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' width='24' height='24'%3E%3Cpath d='M18 6L6 18M6 6l12 12' stroke='%23FFFFFF' stroke-width='2.5' stroke-linecap='round'/%3E%3C/svg%3E") center/13px no-repeat;
    }
    #ft-main-parent-container .ft-remove:disabled { opacity: 0.3; cursor: not-allowed; }

    /* Row remove — grey circle with a centered dark X (SVG, so it's geometrically
       centered at this small size), red with white X on hover. Matches .ft-remove. */
    #ft-main-parent-container .ft-row-remove {
        width: 20px; height: 20px; flex-shrink: 0; border-radius: 50%; border: 0; cursor: pointer; padding: 0;
        background: rgba(211,211,211,1) url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' width='24' height='24'%3E%3Cpath d='M18 6L6 18M6 6l12 12' stroke='%23141414' stroke-width='2.5' stroke-linecap='round'/%3E%3C/svg%3E") center/10px no-repeat;
        transition: all .2s ease;
    }
    #ft-main-parent-container .ft-row-remove:hover:not(:disabled) {
        background: var(--color-danger) url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' width='24' height='24'%3E%3Cpath d='M18 6L6 18M6 6l12 12' stroke='%23FFFFFF' stroke-width='2.5' stroke-linecap='round'/%3E%3C/svg%3E") center/10px no-repeat;
    }
    #ft-main-parent-container .ft-row-remove:disabled { opacity: 0.3; cursor: not-allowed; }

    /* Timer volume — sits in the head row between the time inputs and close button */
    /* The header's one elastic element — it grows to close the gap between the preview
       button and the pills. The basis must be 0, not auto: the shared
       .bws-volume-control sets width:100%, so an auto basis asks for the whole header
       and the resulting shrink is taken out of the title group instead. border-box
       because .fsb-row-volume-control is content-box with .75rem side padding, which
       otherwise spills 24px past whatever width flex resolves for it. */
    #ft-main-parent-container .ft-timer-head-vol { box-sizing: border-box; flex: 1 1 0%; min-width: 0; margin-top: 0; }

    /* Timer preview — small round accent play button in front of the volume slider.
       Holds both glyphs; .ft-preview-on swaps play for stop.
       The library's per-card preview (.mlib-rc-preview, in front of "Add to
       playlist") is the same control and shares these rules rather than
       restating them — both are "audition this one thing, exclusively". */
    #ft-main-parent-container .ft-preview,
    #mlib-main-parent-container .mlib-rc-preview {
        width: 26px; height: 26px; flex-shrink: 0; padding: 0; border: 0; border-radius: 50%;
        display: flex; align-items: center; justify-content: center; cursor: pointer;
        background: #d9b53c; color: #111;
        transition: background-color .2s ease;
    }
    #ft-main-parent-container .ft-preview:hover:not(:disabled),
    #mlib-main-parent-container .mlib-rc-preview:hover:not(:disabled) { background: var(--color-accent, #ffd84b); }
    #ft-main-parent-container .ft-preview:disabled,
    #mlib-main-parent-container .mlib-rc-preview:disabled { opacity: 0.3; cursor: not-allowed; }
    #ft-main-parent-container .ft-preview .ft-preview-play,
    #mlib-main-parent-container .mlib-rc-preview .ft-preview-play { margin-left: 1px; }   /* optically centre the triangle */
    #ft-main-parent-container .ft-preview .ft-preview-stop,
    #mlib-main-parent-container .mlib-rc-preview .ft-preview-stop { display: none; }
    /* Playing: red with the stop square, no ring — the colour change is the whole
       signal, matching the motionless hover everywhere else. Same darker-at-rest /
       lighter-on-hover pair as the yellow state, in red. */
    #ft-main-parent-container .ft-preview.ft-preview-on,
    #mlib-main-parent-container .mlib-rc-preview.ft-preview-on { background: #d32f2f; color: #fff; }
    #ft-main-parent-container .ft-preview.ft-preview-on:hover:not(:disabled),
    #mlib-main-parent-container .mlib-rc-preview.ft-preview-on:hover:not(:disabled) { background: #f44336; }
    #ft-main-parent-container .ft-preview.ft-preview-on .ft-preview-play,
    #mlib-main-parent-container .mlib-rc-preview.ft-preview-on .ft-preview-play { display: none; }
    #ft-main-parent-container .ft-preview.ft-preview-on .ft-preview-stop,
    #mlib-main-parent-container .mlib-rc-preview.ft-preview-on .ft-preview-stop { display: block; }

    /* Frequency rows */
    /* Each row closes with a separator line; the yellow add/duplicate pair sits
       centred on that line (see .ft-insert-row-actions in the shared rules). The
       row's own padding-bottom is what keeps the fields clear of the buttons. */
    /* The bottom padding also has to clear two stacked pairs: the last row's yellow
       buttons hanging 13px below its separator, and the green add-timer pair sitting
       15px above the card's bottom border. */
    #ft-main-parent-container .ft-rows { padding: 0.75rem 0.75rem 3.25rem; display: flex; flex-direction: column; gap: 1.75rem; }
    #ft-main-parent-container .ft-row {
        position: relative;
        padding-bottom: 20px;
        border-bottom: 1px solid var(--border-accent-light);
        display: grid;
        /* badge · preset · hz · function · wave gear · row preview · volume · X */
        grid-template-columns: 28px minmax(120px, 1.15fr) minmax(58px, 0.42fr) minmax(96px, 0.62fr) auto 26px minmax(120px, 1.4fr) 24px;
        gap: 0.5rem; align-items: center;
    }
    #ft-main-parent-container .ft-badge {
        width: 28px; height: 28px; border-radius: 4px; background: var(--color-accent);
        color: #111; font-weight: 600; font-size: 13px; display: flex; align-items: center; justify-content: center;
    }
    #ft-main-parent-container .ft-row .bbp-frequency-input { width: 100%; }
    #ft-main-parent-container .ft-row .bws-dropdown { width: 100%; }
    #ft-main-parent-container .ft-row .bws-volume-control { flex: 1; }
    /* Row volume cell — the shared .fsb-row-volume-control is written for a wrapped,
       full-width row: it carries a top margin and a content-box width: 100% whose
       .75rem side padding then spills 24px past its grid track, under the X button.
       Inside this grid it has to stay in its own track and centre with the other
       cells, so: border-box, no top margin, no 100% flex-basis. */
    #ft-main-parent-container .ft-row .ft-vol-cell {
        box-sizing: border-box;
        margin-top: 0;
        flex-basis: auto;
    }

    /* "No Sound" pause row: dim the function / waveform / volume controls.
       The Hz field stays active so a value can be typed to leave the pause. */
    #ft-main-parent-container .ft-row-nosound .ft-fn-cell,
    #ft-main-parent-container .ft-row-nosound .ft-wave-cell,
    #ft-main-parent-container .ft-row-nosound .ft-vol-cell {
        opacity: 0.35; pointer-events: none;
    }

    /* Manual Hz field — match dropdown height; gold left-aligned "Custom..." placeholder */
    #ft-main-parent-container .ft-hz-input {
        height: 38px; text-align: left; padding: 0.625rem 0.5rem; font-size: 13px; font-variant-numeric: tabular-nums;
    }
    #ft-main-parent-container .ft-hz-input::placeholder { color: var(--color-accent); opacity: 0.85; }
    #ft-main-parent-container .ft-hz-input::-moz-placeholder { color: var(--color-accent); opacity: 0.85; }

    /* Preset trigger — looks like .bws-dropdown but holds a left-aligned label + caret */
    #ft-main-parent-container .ft-preset-trigger {
        display: flex; align-items: center; text-align: left; cursor: pointer;
        color: var(--text-muted);
    }
    #ft-main-parent-container .ft-preset-trigger.has-value { color: var(--color-accent); }
    #ft-main-parent-container .ft-preset-trigger .ft-preset-trigger-text {
        flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
    }

    /* Preset dropdown popup: styled by the shared block near .hpp-preset-dropdown. */
    /* Field-level preset info button (to the right of the preset trigger) */
    #ft-main-parent-container .ft-preset-cell { display: flex; align-items: center; gap: 0.4rem; }
    #ft-main-parent-container .ft-preset-cell .ft-preset-trigger { flex: 1; min-width: 0; }
    /* look + disabled state come from .bws-info-badge */

    /* ── Preset info modal (mirrors mobile FrequencyInfoModal) ── */
    /* Carousel nav — yellow circle buttons beside the modal */
    .ft-info-modal .ft-im-title { flex: 1; color: var(--text-primary, #fff); font-size: 16px; font-weight: 700; }
    .ft-info-modal .ft-im-close {
        width: 28px; height: 28px; flex-shrink: 0; border-radius: 50%; border: 0; cursor: pointer;
        background: rgba(211,211,211,1); color: rgba(20,20,20,0.95); font-size: 12px; line-height: 1;
        display: flex; align-items: center; justify-content: center;
    }
    .ft-info-modal .ft-im-close:hover { background: var(--color-danger); color: #fff; }
    .ft-info-modal .ft-im-underline { height: 1px; background: rgba(128,128,128,0.5); margin: 0 1rem; }
    .ft-info-modal .ft-im-image { width: 100%; aspect-ratio: 1 / 1; object-fit: contain; border-radius: 6px; display: block; background: rgba(255,255,255,0.03); }
    /* Top row: image (left) + main description (right) */
    .ft-info-modal .ft-im-top { display: grid; grid-template-columns: 260px 1fr; gap: 16px; margin-bottom: 0; align-items: start; }
    .ft-info-modal .ft-im-top.no-image { grid-template-columns: 1fr; }
    .ft-info-modal .ft-im-main { min-width: 0; }
    /* Lower section: two columns, separated from the upper part.
       Left col = Play Preview + Details + Usage; Right col = Therapeutic + Safety. */
    .ft-info-modal .ft-im-lower {
        margin-top: 14px; padding-top: 14px; border-top: 1px solid rgba(255,216,75,0.12);
        display: grid; grid-template-columns: 1fr 1fr; gap: 10px; align-items: start;
    }
    .ft-info-modal .ft-im-col { display: flex; flex-direction: column; gap: 10px; min-width: 0; }
    .ft-info-modal .ft-im-col .ft-im-details,
    .ft-info-modal .ft-im-col .ft-im-section,
    .ft-info-modal .ft-im-col .ft-im-preview-row { margin: 0; }
    /* Play Preview button — heads the left column, full width */
    .ft-info-modal .ft-im-preview-btn { display: flex; width: 100%; align-items: center; justify-content: center; gap: 0.4rem; }
    .ft-info-modal .ft-im-preview-btn.ft-preview-on { background: var(--color-danger, #e0564a); border-color: var(--color-danger, #e0564a); color: #fff; }
    .ft-info-modal .ft-im-set-btn { width: 100%; margin-top: 16px; font-size: 14px; font-weight: 500; }
    @media (max-width: 600px) {
        .ft-info-modal .ft-im-top { grid-template-columns: 1fr; }
        .ft-info-modal .ft-im-lower { grid-template-columns: 1fr; }
    }
    .ft-info-modal .ft-im-hz { color: var(--color-accent); font-size: 22px; font-weight: 700; margin-bottom: 2px; }
    .ft-info-modal .ft-im-subtitle { color: rgba(248,249,250,0.9); font-size: 14px; font-weight: 500; margin-bottom: 12px; }
    .ft-info-modal .ft-im-desc { color: rgba(248,249,250,0.85); font-size: 13px; line-height: 20px; margin-bottom: 14px; }
    .ft-info-modal .ft-im-details {
        margin-bottom: 10px; padding: 0.5rem 0.625rem; border-radius: 4px;
        background: rgba(255,216,75,0.05); border: 1px solid rgba(255,216,75,0.12);
    }
    .ft-info-modal .ft-im-detail { color: rgba(248,249,250,0.8); font-size: 12px; line-height: 20px; }
    .ft-info-modal .ft-im-detail-label { color: var(--color-accent); font-weight: 600; }
    .ft-info-modal .ft-im-section {
        margin-top: 10px; padding: 0.625rem; border-radius: 4px; background: rgba(255,255,255,0.03);
    }
    .ft-info-modal .ft-im-section-title {
        color: var(--color-accent); font-size: 13px; font-weight: 600; margin-bottom: 8px;
        padding-bottom: 4px; border-bottom: 1px solid rgba(255,216,75,0.2);
    }
    .ft-info-modal .ft-im-section-item { color: rgba(248,249,250,0.8); font-size: 12px; line-height: 18px; margin-bottom: 4px; }

    /* Waveform mix — one round gear per row; the four shapes live in its dialog.
       Shared by the Frequency / Binaural / Isochronic timer modules, so these are
       deliberately unscoped. */
    #ft-main-parent-container .ft-wave-toggles,
    #bt-main-parent-container .bt-wave-toggles,
    #it-main-parent-container .it-wave-toggles { display: flex; gap: 0.3rem; }
    .bws-wave-gear {
        width: 34px; height: 34px; flex: 0 0 auto; border-radius: 50%;
        display: flex; align-items: center; justify-content: center; cursor: pointer;
        background: var(--bg-dark-lighter); border: 1px solid var(--border-accent-light);
        color: var(--color-accent); transition: all .2s ease;
    }
    .bws-wave-gear:hover {
        background: var(--bg-accent-light); border-color: var(--color-accent);
        transform: rotate(30deg);
    }

    /* ── Waveform mix dialog ─────────────────────────────────────────────────── */
    /* No universal border-box reset in this stylesheet — every box here sets its
       own, or the padding would push the dialog past 94vw and scroll sideways. */
    .bws-wm-dialog, .bws-wm-dialog * { box-sizing: border-box; }
    .bws-wm-dialog {
        position: relative; width: min(560px, 94vw); max-width: 100%;
        max-height: calc(100vh - 100px); overflow-y: auto; overflow-x: hidden;
        padding: 1.25rem 1.4rem 1.1rem; border-radius: 10px;
        background: var(--bg-dark-lighter, #17171a); border: 1px solid var(--border-accent-light);
        box-shadow: 0 18px 50px rgba(0,0,0,0.55);
    }
    .bws-wm-dialog h3 { margin: 0 0 0.15rem; font-size: 17px; color: var(--color-accent); }
    .bws-wm-sub { margin: 0 0 0.85rem; font-size: 12px; color: var(--text-muted); overflow-wrap: anywhere; }
    .bws-wm-close {
        position: absolute; top: 10px; right: 10px; width: 26px; height: 26px; border-radius: 50%;
        display: flex; align-items: center; justify-content: center; cursor: pointer; font-size: 13px;
        background: transparent; border: 1px solid var(--border-accent-light); color: var(--text-muted);
    }
    .bws-wm-close:hover { color: var(--color-accent); border-color: var(--color-accent); }
    .bws-wm-canvas {
        display: block; width: 100%; max-width: 100%; height: 150px; border-radius: 8px;
        background: rgba(0,0,0,0.35); border: 1px solid var(--border-accent-light);
    }
    .bws-wm-note { margin: 0.5rem 0 0.9rem; font-size: 12px; color: var(--text-muted); min-height: 1.2em; }
    .bws-wm-sliders { display: flex; flex-direction: column; gap: 0.55rem; }
    /* min-width:0 on the track — a range input's intrinsic width would otherwise
       stop the 1fr column from shrinking and force the row wider than the dialog. */
    .bws-wm-slider-row { display: grid; grid-template-columns: 30px 68px minmax(0, 1fr) 42px; align-items: center; gap: 0.55rem; }
    .bws-wm-slider-row > * { min-width: 0; }
    .bws-wm-slider-row .bws-wm-slider { width: 100%; min-width: 0; }
    /* Volume is level, not shape — separated from the four waveform sliders. */
    .bws-wm-vol-row { margin-top: 0.35rem; padding-top: 0.7rem; border-top: 1px solid var(--border-accent-light); }
    .bws-wm-vol-glyph svg { width: 20px; height: 20px; }
    .bws-wm-glyph { display: flex; align-items: center; color: var(--color-accent); }
    .bws-wm-glyph svg { width: 28px; height: 18px; }
    .bws-wm-label { font-size: 12px; color: var(--text-primary); }
    .bws-wm-val { font-size: 12px; color: var(--text-muted); text-align: right; font-variant-numeric: tabular-nums; }
    .bws-wm-actions { display: flex; flex-wrap: wrap; align-items: center; gap: 0.55rem; margin-top: 1.1rem; }
    .bws-wm-actions button {
        padding: 7px 13px; border-radius: 5px; cursor: pointer; font-size: 12px; font-weight: 600; font-family: inherit;
        display: inline-flex; align-items: center; gap: 6px;
        background: var(--bg-dark-lighter); border: 1px solid var(--border-accent-light); color: var(--color-accent);
        transition: all .2s ease;
    }
    .bws-wm-actions button:hover:not(:disabled) { border-color: var(--color-accent); background: var(--bg-accent-light); }
    .bws-wm-actions button:disabled { opacity: 0.4; cursor: not-allowed; }
    .bws-wm-done { margin-left: auto; background: var(--color-accent) !important; color: #111 !important; border-color: var(--color-accent) !important; }
    /* Preview toggle reuses each module's row-preview glyph pair — one per state. */
    .bws-wm-preview .ft-preview-stop,
    .bws-wm-preview .bt-preview-stop,
    .bws-wm-preview .it-preview-stop { display: none; }
    .bws-wm-preview.ft-preview-on .ft-preview-play,
    .bws-wm-preview.ft-preview-on .bt-preview-play,
    .bws-wm-preview.ft-preview-on .it-preview-play { display: none; }
    .bws-wm-preview.ft-preview-on .ft-preview-stop,
    .bws-wm-preview.ft-preview-on .bt-preview-stop,
    .bws-wm-preview.ft-preview-on .it-preview-stop { display: inline; }
    .bws-wm-preview.ft-preview-on { border-color: var(--color-accent); background: var(--bg-accent-light); }

    /* ── Binaural split dialog ───────────────────────────────────────────────── */
    /* Built on the waveform-mix dialog's shell (.bws-wm-*); only what differs lives
       here. The chart is taller than the oscilloscope: it carries a labelled Hz axis
       and a time axis, not just a trace. */
    .bws-bs-canvas { height: 200px; }
    .bws-bs-note { min-height: 2.4em; }
    /* Readouts here carry a unit ("10.5 Hz"), so the value column needs more than the
       42px a bare percentage takes in the mix dialog. */
    .bws-bs-dialog .bws-wm-slider-row { grid-template-columns: 30px 46px minmax(0, 1fr) 58px; }
    /* Link toggle sits in the glyph column of the beat slider row — lit while the
       binaural beat follows the pulse rate, dim once it has been set apart. */
    .bws-bs-link {
        display: flex; align-items: center; justify-content: center;
        width: 28px; height: 26px; padding: 0; border-radius: 5px; cursor: pointer;
        background: transparent; border: 1px solid transparent;
        color: var(--text-muted); transition: all .2s ease;
    }
    .bws-bs-link:hover { border-color: var(--color-accent); color: var(--color-accent); }
    .bws-bs-link.active { color: var(--color-accent); border-color: var(--border-accent-light); background: var(--bg-accent-light); }
    .bws-bs-link svg { width: 17px; height: 17px; }
    /* A linked beat is a mirror of the pulse, not an independent value — the label
       says so, and dragging the slider is what breaks the link. */
    .bws-bs-beat-row.bws-bs-linked .bws-wm-label,
    .bws-bs-beat-row.bws-bs-linked .bws-bs-val-beat { color: var(--text-muted); }
    .bws-bs-enhance svg { width: 16px; height: 16px; }
    .bws-bs-enhance.active { border-color: var(--color-accent); background: var(--bg-accent-light); }
    /* Binaural beat preset + Hz fields, between the Enhance toggle and the gear. Always
       laid out: the Enhance toggle disables them rather than hiding them, so toggling
       never reflows the row. They take the slack in the cell; the buttons stay fixed. */
    #it-main-parent-container .it-bb-cell {
        display: flex; align-items: center; gap: 0.4rem; min-width: 0; flex: 1 1 auto;
    }
    #it-main-parent-container .it-bb-cell .it-preset-cell {
        display: flex; align-items: center; gap: 0.3rem; min-width: 0; flex: 1 1 55%;
    }
    #it-main-parent-container .it-bb-cell .it-hz-cell { min-width: 0; flex: 1 1 45%; }
    #it-main-parent-container .it-bb-cell .it-preset-trigger,
    #it-main-parent-container .it-bb-cell .it-hz-input { min-width: 0; width: 100%; }
    /* Off = present but inert. Dimmed to match the module's other disabled clusters
       (.it-carrier-nosound); the real `disabled` attributes do the blocking. */
    #it-main-parent-container .it-bb-cell.it-bb-off { opacity: 0.35; }
    #it-main-parent-container .it-be-gear:disabled,
    #it-main-parent-container .it-be-cell .bws-info-badge:disabled { opacity: 0.35; cursor: not-allowed; }
    /* .bws-wave-gear:hover rotates and lights up — neither should happen while inert. */
    #it-main-parent-container .it-be-gear:disabled:hover {
        transform: none; background: var(--bg-dark-lighter); border-color: var(--border-accent-light);
    }
    /* Linked to the pulse = no value of its own; the placeholder carries the followed
       number, so the field reads as tracking rather than empty. */
    #it-main-parent-container .it-bb-cell.it-bb-linked .it-hz-input::placeholder { font-style: italic; opacity: 0.75; }

    /* The split gear keeps the plain round .bws-wave-gear look of the waveform-mix gear
       on the tone sub-row — the two are the same kind of control, so they read the same.
       Only the disabled state is added here. */

    @media (max-width: 760px) {
        #ft-main-parent-container .ft-row {
            grid-template-columns: 28px 1fr 1fr auto 24px;
            grid-template-areas:
                "badge sel  sel  sel x"
                "badge hz   fn   fn  x"
                "badge wave prev vol vol";
            row-gap: 0.45rem;
        }
        #ft-main-parent-container .ft-badge { grid-area: badge; }
        #ft-main-parent-container .ft-row .ft-preset-cell { grid-area: sel; }
        #ft-main-parent-container .ft-row .ft-hz-cell { grid-area: hz; }
        #ft-main-parent-container .ft-row .ft-fn-cell { grid-area: fn; }
        #ft-main-parent-container .ft-row .ft-wave-cell { grid-area: wave; justify-self: start; }
        #ft-main-parent-container .ft-row .ft-row-preview { grid-area: prev; justify-self: start; }
        #ft-main-parent-container .ft-row .ft-vol-cell { grid-area: vol; }
        #ft-main-parent-container .ft-row .ft-row-remove { grid-area: x; }
    }

/* ───── from GUIDED-MEDITATIONS-MODULE/index.html ───── */
/* Title bar */
    #gm-section-playlist { margin-top: 1.5rem; }

    /* ── Presets: tabs + grid (mirrors Frequency Presets) ─────────────────── */
    #gm-main-parent-container .alb-tab.active { background: var(--color-accent); color: #111; border-color: var(--color-accent); }

    @media (max-width: 900px) { #gm-main-parent-container .alb-grid { grid-template-columns: repeat(2, 1fr); } }
    @media (max-width: 600px) { #gm-main-parent-container .alb-grid { grid-template-columns: 1fr; } }

    #gm-main-parent-container .alb-card {
        position: relative; display: flex; gap: 10px; min-height: 160px; cursor: pointer; overflow: hidden;
        background: var(--bg-dark-lighter); border: 1px solid var(--border-accent-light); border-radius: 4px;
        transition: border-color .2s ease, box-shadow .2s ease;
    }
    #gm-main-parent-container .alb-card:hover { border-color: var(--color-accent); box-shadow: 0 0 8px var(--glow-accent); }
    #gm-main-parent-container .alb-card-cover { width: 84px; flex-shrink: 0; object-fit: cover; background: #111; align-self: stretch; }
    #gm-main-parent-container .alb-card-body { flex: 1; min-width: 0; padding: 10px 10px 10px 0; display: flex; flex-direction: column; gap: 4px; }
    #gm-main-parent-container .alb-card-top { display: flex; align-items: flex-start; gap: 6px; }
    #gm-main-parent-container .alb-card-title { flex: 1; font-size: 14px; font-weight: 600; color: var(--text-primary, #fff); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
    /* .alb-info-btn look comes from .bws-info-badge */
    #gm-main-parent-container .alb-card-desc { font-size: 12px; color: var(--text-bright, rgba(255,255,255,0.8)); line-height: 1.4; display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; }
    #gm-main-parent-container .alb-card-empty { border: 1px dashed var(--border-accent-light); border-radius: 4px; min-height: 160px; opacity: 0.22; }

    .alb-badge-free { background: rgba(75,200,100,0.25); }
    .alb-badge-basic { background: rgba(75,150,255,0.25); }
    .alb-badge-premium { background: rgba(180,75,255,0.25); }
    .alb-badge-paid { background: rgba(255,216,75,0.18); }

    #gm-main-parent-container .alb-pager { display: flex; align-items: center; justify-content: center; gap: 14px; margin-top: 1rem; }
    #gm-main-parent-container .alb-pager button { padding: 6px 14px; border-radius: 4px; cursor: pointer; font-weight: 600; background: var(--bg-dark-lighter); border: 1px solid var(--border-accent-light); color: var(--color-accent); }
    #gm-main-parent-container .alb-pager button:disabled { opacity: 0.3; cursor: not-allowed; }
    #gm-main-parent-container .alb-pager-info { font-size: 13px; color: var(--text-muted); }
    #gm-main-parent-container .alb-personal-empty,
    #gm-main-parent-container .alb-grid-msg { grid-column: 1 / -1; text-align: center; color: var(--text-muted); padding: 2rem 1rem; font-size: 14px; line-height: 1.6; }

    /* ── Album info modal (cover/title/badge/description + track list) ─────── */

    /* ── Playlist segment (mirrors My Library Playlist) ───────────────────── */
    .alb-pl-card.alb-pl-dragging { opacity: 0.45; }
    .alb-pl-card.alb-pl-drop-above { box-shadow: inset 0 3px 0 0 var(--color-accent, #ffd84b); }
    .alb-pl-card.alb-pl-drop-below { box-shadow: inset 0 -3px 0 0 var(--color-accent, #ffd84b); }
    .alb-pl-drag-handle:hover { color: rgba(255,216,75,0.85); }
    .alb-pl-drag-handle:active { cursor: grabbing; }

    /* Row player control bar */
    .alb-play-btn:hover { background: rgba(255,216,75,0.14); }
    /* Seek + IN/OUT chevron range track (per-clip start/stop trim) */
    /* Passed-time fill clamped to the chevron window: track color before IN, accent only IN→position */
    .alb-seek-col .alb-seek { background: linear-gradient(to right, var(--slider-track-bg, rgba(255,255,255,0.15)) 0%, var(--slider-track-bg, rgba(255,255,255,0.15)) var(--in, 0%), var(--color-accent) var(--in, 0%), var(--color-accent) var(--val, 0%), var(--slider-track-bg, rgba(255,255,255,0.15)) var(--val, 0%)) !important; }
    .alb-seek-handle:hover, .alb-seek-handle:active { border-bottom-color: #ffd84b; }
    .alb-trim-dur { color: #ffd84b !important; }

    .alb-pl-transport .alb-pl-master-slider { flex: 1; min-width: 0; width: auto; }

    /* ── Audio editor (the "Edit audio" button on the edit dialog) ────────── */
    /* The button itself. Accent-filled rather than outlined: it is the one
       control in that dialog that opens a different tool instead of editing a
       field, and it should not read as another text input's neighbour. */
    .gm-ed-audiorow { margin: 14px 0 4px; }
    .gm-ed-audiobtn {
        background: var(--color-accent, #ffd84b);
        color: #1a1a1a;
        border: none;
        border-radius: 6px;
        padding: 9px 18px;
        font-size: 13px;
        font-weight: 700;
        letter-spacing: 0.01em;
        cursor: pointer;
        transition: filter .15s ease;
    }
    .gm-ed-audiobtn:hover { filter: brightness(1.08); }

    /* Wider than the dialog it opens from — a waveform cut into forty segments
       is unusable at 380px, and the edit dialog's own max-width has to be
       outranked by carrying .fp-save-dialog on the selector. */
    .fp-save-dialog.gmx-modal {
        max-width: 1180px;
        width: calc(100vw - 60px);
        max-height: calc(100vh - 60px);
        overflow-y: auto;
    }
    /* "Edit audio • <clip>". The heading keeps the accent it wears in every
       dialog of this family; the name beside it is white, so the fixed part and
       the part that changes per track are told apart without a second line.
       Qualified past .fp-save-dialog h3, which sets the accent for all of it. */
    .gmx-modal h3 {
        display: flex;
        align-items: baseline;
        /* No wrapping: the name shrinks and ellipsises instead of dropping
           to a second line and taking the dialog's height with it. */
        gap: 0 8px;
        /* The close button lives in the top-right corner, and a long clip name
           would otherwise run underneath it. */
        padding-right: 32px;
    }
    .gmx-modal h3 .gmx-h3-sep { color: var(--text-muted); }
    /* Same size and colour as the name, lighter: it is a fact about the clip
       rather than its identity, and it must not compete with it. `flex: none`
       so the name is what gives way when the heading runs out of room. */
    .gmx-modal h3 .gmx-h3-count {
        flex: none;
        color: var(--color-text, #fff);
        font-weight: 400;
        font-variant-numeric: tabular-nums;
    }
    .gmx-modal h3 .gmx-h3-name {
        color: var(--color-text, #fff);
        font-weight: 600;
        /* One line, cut with an ellipsis: a long title must not push the
           heading into two rows and move everything below it. */
        min-width: 0;
        overflow: hidden;
        text-overflow: ellipsis;
        white-space: nowrap;
    }

    /* Fixed height, not a minimum, and the same height empty as full: a
       segment's text can be the entire script, and a box that grew to fit one
       would push the waveform out from under the pointer that is hovering it —
       and, past the modal's own max-height, start scrolling the whole dialog.
       Two lines is what the tallest state gets; the rest is in the title. */
    .gmx-hint {
        box-sizing: border-box;
        height: 47px;                  /* 2 x 1.4 line-height at 12.5px + padding */
        display: flex;
        align-items: center;
        padding: 6px 10px;
        margin-bottom: 6px;
        border-radius: 5px;
        background: var(--bg-dark-light, rgba(255,255,255,0.04));
        font-size: 12.5px;
        line-height: 1.4;
    }
    /* The clamp lives on the span, not the box: the box is a flex centring
       frame, and -webkit-box would take that job away from it. */
    .gmx-hint > span {
        display: -webkit-box;
        -webkit-box-orient: vertical;
        -webkit-line-clamp: 2;
        line-clamp: 2;
        overflow: hidden;
    }
    .gmx-hint-idle { color: var(--text-muted); }
    .gmx-hint-text { color: var(--color-accent, #ffd84b); }

    /* The waveform and the gain fader beside it. The fader is a sibling rather
       than an overlay: it has to be grabbable at the full height of the
       waveform, and anything drawn over the canvas would be competing with the
       drags that canvas already owns. */
    .gmx-stage-row { display: flex; align-items: stretch; gap: 8px; }
    .gmx-stage-col { flex: 1; min-width: 0; }

    /* ── Output gain fader ────────────────────────────────────────────────── */
    .gmx-vgain {
        flex: 0 0 auto;
        width: 52px;
        display: flex;
        flex-direction: column;
        align-items: center;
        gap: 4px;
        padding: 4px 0;
        border: 1px solid var(--border-accent-light, rgba(255,216,75,0.2));
        border-radius: 6px;
        background: var(--bg-dark-light, rgba(255,255,255,0.04));
    }
    /* Vertical range input. writing-mode is the standard spelling; the
       -webkit-appearance line is the older one, kept because a browser that
       ignores the first and the second alike would render this lying on its
       side across the layout rather than merely unstyled. */
    .gmx-vgain-sl {
        flex: 1;
        width: 24px;
        min-height: 120px;
        writing-mode: vertical-lr;
        direction: rtl;
        -webkit-appearance: slider-vertical;
        appearance: slider-vertical;
        accent-color: var(--color-accent, #ffd84b);
        cursor: pointer;
        margin: 0;
    }
    .gmx-vgain-sl:disabled { opacity: 0.35; cursor: default; }
    /* Tabular and fixed-width so the column does not twitch as the number runs
       between 0.0 and +12.0. */
    .gmx-vgain-val, .gmx-vgain-pk {
        font-size: 11px;
        font-variant-numeric: tabular-nums;
        color: var(--text-muted);
        line-height: 1.2;
    }
    .gmx-vgain-val.gmx-vgain-active { color: var(--color-accent, #ffd84b); font-weight: 700; }
    /* The peak the fader has put the file at. Red once it is past the ceiling —
       not an error, just the signal that the limiter is now doing the work, and
       the same colour as the -1 dB line drawn across the waveform. */
    .gmx-vgain-pk.gmx-vgain-over { color: #ff6b6b; }
    .gmx-vgain-reset { min-width: 26px; padding: 3px 6px; font-size: 11px; }

    .gmx-stage { position: relative; }
    /* The canvas sizes itself from clientWidth, so the height is the only thing
       CSS settles; it must match CANVAS_H in gm-audio-edit.js. */
    .gmx-stage #gmx-canvas {
        display: block;
        width: 100%;
        height: 248px;
        border: 1px solid var(--border-accent-light, rgba(255,216,75,0.2));
        border-radius: 6px;
        touch-action: none;          /* pointer drags must not scroll the page */
    }
    .gmx-loading {
        position: absolute; inset: 0;
        display: flex; align-items: center; justify-content: center;
        color: var(--text-muted); font-size: 13px;
    }

    /* ── Toolbar (above the waveform) ─────────────────────────────────────── */
    .gmx-tools { display: flex; align-items: center; gap: 8px; margin-bottom: 6px; flex-wrap: wrap; }
    .gmx-tools .gmx-sp { flex: 1; }

    /* What the mouse does over the waveform, sitting directly above it. This
       is a LEGEND, not a toolbar: the mouse button is the tool, so nothing
       here is clickable and no entry is ever the selected one. Accent through-
       out rather than dimmed — it is a key to the surface below it, and read
       once. */
    .gmx-legend {
        display: flex;
        align-items: center;
        flex-wrap: wrap;
        gap: 4px 10px;
        margin: 0 0 6px;
        font-size: 11.5px;
        color: var(--color-accent, #ffd84b);
    }
    .gmx-leg { display: inline-flex; align-items: center; gap: 6px; }
    /* The mouse glyph rides on the legend's own colour and needs no rule. */
    .gmx-leg .gmx-mico { flex: none; display: block; }
    .gmx-leg-sep { opacity: 0.5; }

    /* Leads the row, with Auto-cut behind it: it does the same job in one step,
       so it is what you reach for first and the manual pass is the fallback.
       How it LOOKS is not decided here: .bws-ai-btn at the foot of this sheet
       dresses every credit-spending button in the app, this one included. */

    /* Extend/shorten all pauses. Label above the slider rather than beside it,
       so the row does not get much wider than the button next to it. */
    .gmx-pause {
        display: inline-grid;
        grid-template-columns: 150px auto auto;
        grid-template-rows: auto auto;
        align-items: center;
        gap: 0 8px;
        margin-left: 10px;
    }
    .gmx-pause label {
        grid-column: 1 / -1;
        font-size: 10.5px;
        letter-spacing: 0.03em;
        text-transform: uppercase;
        color: var(--text-muted);
        margin-bottom: 2px;
        cursor: pointer;
    }
    .gmx-pause .gmx-padj { width: 150px; }
    .gmx-pause .gmx-padj:disabled { opacity: 0.35; cursor: default; }
    /* Tabular and fixed-width so the row does not twitch as the number runs
       between -5.0 and +5.0. */
    .gmx-padj-val {
        min-width: 42px;
        font-size: 11px;
        font-variant-numeric: tabular-nums;
        color: var(--text-muted);
    }
    /* Lit only when the slider is actually holding an offset, so "is anything
       applied right now" is readable without looking at the thumb. */
    .gmx-padj-val.gmx-padj-active { color: var(--color-accent, #ffd84b); font-weight: 700; }
    .gmx-padj-reset { min-width: 28px; padding: 4px 8px; font-size: 11px; }

    /* ── Voice cleanup ────────────────────────────────────────────────────── */
    /* Its own row, separated by a rule, because it rewrites the audio rather
       than the arrangement of it — the same reason it is outside Undo. */
    .gmx-clean {
        display: flex;
        align-items: center;
        gap: 8px;
        flex-wrap: wrap;
        margin-bottom: 6px;
        padding-top: 8px;
        border-top: 1px solid var(--border-accent-light, rgba(255,216,75,0.15));
    }
    .gmx-clean-lbl {
        font-size: 10.5px;
        letter-spacing: 0.03em;
        text-transform: uppercase;
        color: var(--text-muted);
    }
    .gmx-sel {
        background: var(--bg-dark-light, rgba(255,255,255,0.06));
        color: var(--color-text, #fff);
        border: 1px solid var(--border-accent-light, rgba(255,216,75,0.25));
        border-radius: 5px;
        padding: 5px 8px;
        font-size: 12px;
        cursor: pointer;
    }
    .gmx-sel:disabled { opacity: 0.4; cursor: default; }
    /* What the survey measured, kept next to the button that produced it — it
       is the only way to tell whether the cleanup found the right resonance. */
    .gmx-btn.gmx-clean-on {
        border-color: var(--color-accent, #ffd84b);
        background: rgba(255,216,75,0.14);
        font-weight: 600;
    }
    .gmx-clean-info { font-size: 11px; color: var(--text-muted); font-variant-numeric: tabular-nums; }

    /* ── The scroll window ────────────────────────────────────────────────── */
    /* A view of the whole meditation, with the on-screen part lit. Hard against
       the canvas above it so it reads as that waveform's own scrollbar. The
       track is the full file; the window is what you can see. */
    .gmx-scroll {
        position: relative;
        height: 22px;
        margin-top: 4px;
        /* Inset by the dB gutter so the bar spans the WAVEFORM, not the canvas
           element. --gmx-axis is set from AXIS_W in gm-audio-edit.js. */
        margin-left: var(--gmx-axis, 44px);
        border: 1px solid var(--border-accent-light, rgba(255,216,75,0.18));
        border-radius: 5px;
        background: rgba(255,255,255,0.045);
        cursor: pointer;
        touch-action: none;          /* dragging the window must not scroll the page */
        overflow: hidden;
    }
    .gmx-scroll-win {
        position: absolute;
        top: 0; bottom: 0;
        /* Never narrower than both handles plus something to grab between them,
           or zooming deep in leaves a window too small to pick up again. */
        min-width: 26px;
        background: rgba(255,216,75,0.26);
        border: 1px solid var(--color-accent, #ffd84b);
        border-radius: 4px;
        box-sizing: border-box;
        cursor: grab;
        display: flex;
        align-items: center;
        justify-content: space-between;
    }
    .gmx-scroll-win:active { cursor: grabbing; }
    /* At Fit there is nothing to pan, but the edges still zoom. */
    .gmx-scroll-win.gmx-scroll-full { cursor: default; background: rgba(255,216,75,0.14); }

    /* Handles sit inside the window's own edges, so the whole grab zone is
       visibly part of the window rather than floating beside it. */
    .gmx-scroll-h {
        width: 9px;
        align-self: stretch;
        cursor: ew-resize;
        background: var(--color-accent, #ffd84b);
        flex: 0 0 auto;
    }
    .gmx-scroll-hl { border-radius: 3px 0 0 3px; }
    .gmx-scroll-hr { border-radius: 0 3px 3px 0; }
    /* Three lines in the middle: the affordance that says "drag me sideways". */
    .gmx-scroll-grip {
        flex: 1 1 auto;
        height: 8px;
        margin: 0 4px;
        min-width: 0;
        background-image: repeating-linear-gradient(
            to right, rgba(255,216,75,0.75) 0 1px, transparent 1px 4px);
        pointer-events: none;
    }

    .gmx-zoomlbl {
        min-width: 42px;
        text-align: center;
        font-size: 11px;
        font-variant-numeric: tabular-nums;
        color: var(--text-muted);
    }
    /* The − and + are single glyphs; without a floor they come out narrower
       than every other button in the row. */
    .gmx-transport .gmx-btn#gmx-zout,
    .gmx-transport .gmx-btn#gmx-zin { min-width: 32px; font-size: 14px; line-height: 1; padding: 6px 8px; }

    .gmx-transport { display: flex; align-items: center; gap: 8px; margin-top: 10px; flex-wrap: wrap; }
    .gmx-transport .gmx-sp { flex: 1; }
    .gmx-btn {
        background: transparent;
        color: var(--color-text, #fff);
        border: 1px solid var(--border-accent-light, rgba(255,216,75,0.25));
        border-radius: 5px;
        padding: 6px 12px;
        font-size: 12px;
        cursor: pointer;
    }
    .gmx-btn:hover:not(:disabled) { background: rgba(255,216,75,0.12); }
    .gmx-btn:disabled { opacity: 0.4; cursor: default; }
    .gmx-btn.gmx-play { min-width: 84px; font-weight: 600; }
    .gmx-time { font-variant-numeric: tabular-nums; font-size: 12px; color: var(--text-muted); }

    .gmx-status { min-height: 18px; margin-top: 8px; font-size: 12px; color: var(--text-muted); }
    .gmx-status-info  { color: var(--color-accent, #ffd84b); }
    .gmx-status-ok    { color: #6bd07a; }
    .gmx-status-warn  { color: #ffb84d; }
    .gmx-status-error { color: #ff6b6b; }

    /* ── Transcript (meditation info modal, under the description) ────────── */
    /* Borrows .alb-modal-tracks for the rule + uppercase heading, so it reads as
       a sibling of "In Playlist" rather than a second kind of section. */
    .gm-tr-wrap { margin-top: 10px; }
    /* Scrolls in its own box: a five-minute narration is a hundred lines, and
       without a ceiling it pushes the action row out of the dialog entirely. */
    .gm-tr-body {
        max-height: 220px;
        overflow-y: auto;
        padding-right: 4px;
    }
    .gm-tr-line { display: flex; gap: 8px; align-items: baseline; padding: 2px 0; font-size: 12.5px; line-height: 1.5; }
    /* Fixed column so the words start on one edge down the whole transcript —
       tabular figures keep 0:09 and 0:10 the same width. A line whose start time
       was never aligned still reserves the column rather than shifting its text. */
    .gm-tr-at {
        flex: 0 0 34px;
        text-align: right;
        color: var(--color-accent, #ffd84b);
        font-variant-numeric: tabular-nums;
        font-size: 11px;
        opacity: 0.75;
    }
    .gm-tr-text { flex: 1; min-width: 0; color: rgba(248,249,250,0.86); }
    /* The Gemini side: the stored script, untimed and whitespace-significant. */
    .gm-tr-script {
        white-space: pre-wrap;
        word-break: break-word;
        font-size: 12.5px;
        line-height: 1.55;
        color: rgba(248,249,250,0.86);
    }

    /* ── Meditation recipe dialog (the "Recipe" button on a saved track) ──── */
    /* What a Gemini TTS meditation was generated from. Uses the shared
       .fp-modal-overlay / .fp-save-dialog shell, so only the width and the two
       text blocks need rules here. Both selectors carry .fp-save-dialog too, to
       outrank its own max-width of 380px — a script is unreadable that narrow. */
    .fp-save-dialog.gm-rec-modal {
        max-width: 720px;
        max-height: calc(100vh - 100px);
        overflow-y: auto;
    }
    .fp-save-dialog.gm-rec-modal .gm-rec-val {
        color: var(--color-text, #fff);
        font-size: 13px;
        line-height: 1.5;
        margin: 0 0 12px;
    }
    /* The direction and the script are whitespace-significant — a meditation
       script IS its line breaks, and reflowing them would misrepresent what was
       sent. Each scrolls in its own box rather than making the dialog endless. */
    .fp-save-dialog.gm-rec-modal .gm-rec-pre {
        white-space: pre-wrap;
        word-break: break-word;
        max-height: 240px;
        overflow-y: auto;
        padding: 9px;
        border: 1px solid var(--border-accent-light);
        border-radius: 5px;
        background: var(--bg-dark-light);
        font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
        font-size: 12px;
    }

    /* ── Guided Meditation Builder (inside the AI Meditation Creator dialog) ── */
    .gmb-dialog .gmb-meta-row { display: flex; gap: 12px; align-items: flex-end; flex-wrap: wrap; }
    .gmb-dialog .gmb-field { display: flex; flex-direction: column; gap: 6px; }
    .gmb-dialog .gmb-meta-row .gmb-field { flex: 1 1 200px; min-width: 0; }
    .gmb-dialog .gmb-field label { font-size: 12px; font-weight: 600; color: var(--text-muted); letter-spacing: 0.02em; }
    .gmb-dialog .gmb-input,
    .gmb-dialog .gmb-textarea {
        width: 100%; box-sizing: border-box; padding: 9px 12px; font-size: 14px; font-family: inherit;
        background: var(--bg-dark-lighter, #1a1a1a); color: var(--text-primary, #fff);
        border: 1px solid var(--border-accent-light, rgba(255,216,75,0.2)); border-radius: 8px;
        transition: border-color .15s ease, box-shadow .15s ease;
    }
    .gmb-dialog .gmb-input:focus,
    .gmb-dialog .gmb-textarea:focus { outline: none; border-color: var(--color-accent, #ffd84b); box-shadow: 0 0 0 2px var(--glow-accent, rgba(255,216,75,0.18)); }
    .gmb-dialog .gmb-generate-btn { flex: 0 0 auto; white-space: nowrap; }
    .gmb-dialog .gmb-generate-btn:disabled { opacity: 0.6; cursor: default; }
    .gmb-dialog .gmb-script-field { margin-top: 14px; }
    .gmb-dialog .gmb-textarea { resize: vertical; min-height: 200px; line-height: 1.6; white-space: pre-wrap; }
    /* The description sits under the title, full width. It shares .gmb-textarea
       with the script box, so it has to drop that box's 200px floor and let its
       rows="3" decide the height instead. */
    .gmb-dialog .gmb-desc-field { margin-top: 12px; }
    .gmb-dialog .gmb-desc-textarea { min-height: 0; }
    /* Said before the Create button, about what that button is about to do: a
       split meditation is several generations and several charges. Accent-tinted
       rather than red — it is a heads-up, not a problem. */
    .gmb-dialog .gm-ai-parts-note,
    .bws-ai-builder .gm-ai-parts-note {
        margin: 0 0 10px; padding: 9px 12px; border-radius: 8px;
        font-size: 13px; line-height: 1.5; color: var(--text-bright, rgba(255,255,255,0.8));
        background: var(--bg-accent-light, rgba(255,216,75,0.14));
        border: 1px solid var(--border-accent-light, rgba(255,216,75,0.2));
    }
    /* A part that failed, and the one still running. Both sit in the result list
       where their cards would have been, so the series reads as a whole. */
    .bws-ai-builder .gm-ai-part-failed {
        border-color: var(--color-danger, #d33);
        background: var(--bg-danger, rgba(220,53,69,0.16));
    }
    .bws-ai-builder .gm-ai-part-failed .mlib-result-thumb-placeholder { color: var(--color-danger, #d33); }
    .bws-ai-builder .gm-ai-part-pending { opacity: 0.75; }
    .bws-ai-builder .gm-ai-part-pending .mlib-spinner { margin: 0 12px 0 4px; }

    /* The “Part 2 of 6” rule between groups of result cards. */
    .bws-ai-builder .gm-ai-part-head { margin-top: 18px; }
    .bws-ai-builder .gm-ai-part-head:first-child { margin-top: 0; }
    .gmb-dialog .gmb-status { margin-top: 10px; font-size: 13px; line-height: 1.5; color: var(--text-muted); }
    .gmb-dialog .gmb-status-info { color: var(--color-accent, #ffd84b); }
    .gmb-dialog .gmb-status-warn { color: #ff9a8b; }
    @media (max-width: 640px) {
        .gmb-dialog .gmb-generate-btn { flex: 1 1 100%; }
    }

/* ───── from ISOCHRONIC-TIMERS-MODULE-PRO/index.html ───── */
/* ===== Isochronic Presets section ===== */
    #ip-presets-parent-container { margin-bottom: 1.5rem; }

    #ip-presets-parent-container .ip-tabs { display: flex; gap: 8px; margin-bottom: 1rem; }
    #ip-presets-parent-container .ip-tab.active { background: var(--color-accent); color: #111; border-color: var(--color-accent); }

    #ip-presets-parent-container .ip-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 12px; }
    @media (max-width: 900px) { #ip-presets-parent-container .ip-grid { grid-template-columns: repeat(2, 1fr); } }
    @media (max-width: 600px) { #ip-presets-parent-container .ip-grid { grid-template-columns: 1fr; } }

    #ip-presets-parent-container .ip-card {
        position: relative; display: flex; gap: 10px; min-height: 160px; cursor: pointer; overflow: hidden;
        background: var(--bg-dark-lighter); border: 1px solid var(--border-accent-light); border-radius: 4px;
        transition: border-color .2s ease, box-shadow .2s ease;
    }
    #ip-presets-parent-container .ip-card:hover { border-color: var(--color-accent); box-shadow: 0 0 8px var(--glow-accent); }
    #ip-presets-parent-container .ip-card-cover { width: 84px; flex-shrink: 0; object-fit: cover; background: #111; align-self: stretch; }
    #ip-presets-parent-container .ip-card-body { flex: 1; min-width: 0; padding: 10px 10px 10px 0; display: flex; flex-direction: column; gap: 4px; }
    #ip-presets-parent-container .ip-card-top { display: flex; align-items: flex-start; gap: 6px; }
    #ip-presets-parent-container .ip-card-title { flex: 1; font-size: 14px; font-weight: 600; color: var(--text-primary, #fff); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
    /* .ip-info-btn look comes from .bws-info-badge */
    #ip-presets-parent-container .ip-card-desc { font-size: 12px; color: var(--text-bright, rgba(255,255,255,0.8)); line-height: 1.4; display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; }
    #ip-presets-parent-container .ip-card-empty { border: 1px dashed var(--border-accent-light); border-radius: 4px; min-height: 160px; opacity: 0.22; }

    .ip-badge-free { background: rgba(75,200,100,0.25); }
    .ip-badge-basic { background: rgba(75,150,255,0.25); }
    .ip-badge-premium { background: rgba(180,75,255,0.25); }
    .ip-badge-paid { background: rgba(255,216,75,0.18); }
    .ip-badge-personal { background: rgba(255,255,255,0.14); }

    #ip-presets-parent-container .ip-pager { display: flex; align-items: center; justify-content: center; gap: 14px; margin-top: 1rem; }
    #ip-presets-parent-container .ip-pager button { padding: 6px 14px; border-radius: 6px; cursor: pointer; font-weight: 600; background: var(--bg-dark-lighter); border: 1px solid var(--border-accent-light); color: var(--color-accent); }
    #ip-presets-parent-container .ip-pager button:disabled { opacity: 0.3; cursor: not-allowed; }
    #ip-presets-parent-container .ip-pager-info { font-size: 13px; color: var(--text-muted); }
    #ip-presets-parent-container .ip-grid-msg { grid-column: 1 / -1; text-align: center; color: var(--text-muted); padding: 2rem 1rem; font-size: 14px; line-height: 1.6; }

    /* Preset info modal */
    .ip-modal-body { padding: 14px 18px 4px; overflow-y: auto; }
    .ip-modal-action { padding: 16px; flex-shrink: 0; }
    .ip-modal-load:disabled { opacity: 0.5; cursor: default; }

    /* Save dialog (reuses .ip-modal-overlay) */

    /* Tags — card chips, filter bar (Personal), save-dialog entry */

    /* Full filter console (title / tags / date / duration) — the timer preset bars
       carry the same fields as the Preset Packs bar, so the three modules and the
       pack grid all filter the same way. */

/* ── Filter-bar Clear, armed ──────────────────────────────────────────────────
   With something actually filtered the button stops being a dim outline and
   takes the accent fill the primary buttons use ("Create Custom Pack" and
   friends), so it reads as the way back to the full list. */
.mlib-fbar-clear.is-active,
.bws-fbar-clear.is-active {
    background: var(--color-accent, #ffd84b);
    border-color: var(--color-accent, #ffd84b);
    color: #000;
}
.mlib-fbar-clear.is-active:hover,
.bws-fbar-clear.is-active:hover {
    filter: brightness(1.08);
    color: #000;
}

/* ── Filter-bar date fields ───────────────────────────────────────────────────
   An empty <input type="date"> still prints "dd/mm/yyyy" in the input's own text
   colour, which put a white, filled-in-looking value next to genuinely empty
   fields. Until a date is picked the field carries .has-value = false and its
   text sits at the same muted grey as the "Search title …" placeholder beside
   it; the picker icon is chrome rather than data, so it stays dimmed either way. */
.mlib-fbar-input[type="date"]:not(.has-value),
.bws-fbar-input[type="date"]:not(.has-value) {
    color: var(--text-muted, #888);
}
.mlib-fbar-input[type="date"]::-webkit-calendar-picker-indicator,
.bws-fbar-input[type="date"]::-webkit-calendar-picker-indicator {
    cursor: pointer;
    opacity: 0.45;
    transition: opacity .15s ease;
}
.mlib-fbar-input[type="date"]:hover::-webkit-calendar-picker-indicator,
.bws-fbar-input[type="date"]:hover::-webkit-calendar-picker-indicator {
    opacity: 0.8;
}
    .ip-save-dialog .ip-tags-input:focus { outline: none; }
    .ip-tag-remove:hover { background: #f44336; color: #fff; }

/* ───── from ISOCHRONIC-TIMERS-MODULE-PRO/index.html ───── */
/* ===== Frequency Timers — only layout the shared system doesn't cover.
       Fonts/colors come from the shared .bws-* classes + design tokens. ===== */

    /* ── Session position track: segmented bar (one rounded bar per timer) ── */
    #it-main-parent-container .it-seek-segs { position: absolute; left: 0; right: 0; top: 50%; height: 6px; transform: translateY(-50%); display: flex; align-items: center; gap: 1px; pointer-events: none; }

    /* Timer cards */
    /* Gap / bottom margin leave room for the green add-timer pair, which hangs off
       each card's bottom border (see .it-add-timer-actions in the shared rules). */
    /* margin-top leaves room for the lead insert bar, which hangs above the first card. */
    #it-main-parent-container #it-timers { display: flex; flex-direction: column; gap: 2.5rem; margin-top: 1.75rem; margin-bottom: 1.75rem; }
    #it-main-parent-container .it-timer {
        position: relative;
        background: var(--bg-dark-light);
        border: 1px solid var(--border-accent-light);
        border-radius: 4px;
        transition: border-color .2s ease, box-shadow .2s ease;
    }
    /* Two states, same pair as a playlist row. ACTIVE is the cued card — the one
       a press of play would run: solid accent border. RUNNING is that card with the
       transport moving: the border breathes and the card takes a faint golden fill.
       The fill is opaque on purpose — a translucent tint would show the page's
       background sweep through, so the first and last card would not match. */
    #it-main-parent-container .it-timer.it-timer-active {
        border-color: var(--color-accent);
        box-shadow: 0 0 8px var(--glow-accent);
    }
    #it-main-parent-container .it-timer.it-timer-running {
        background: #17140b;
        animation: bws-pl-glow 2.6s ease-in-out infinite;
    }
    /* ── Pause card: a silent gap between timers ──────────────────────────────
       The same container the playlist modules give a pause row — the chips and the
       custom H:M:S share their rules outright (see the pause block in the shared
       playlist section), so the two read as one control wherever it appears. It
       stays a .it-timer so the green discs still hang off its bottom edge; the card
       look is restated here because .it-timer is scoped to this container and would
       otherwise win. */
    #it-main-parent-container .it-timer--pause:not(.it-timer-running) {
        background: #121212;
    }
    /* The green discs hang 13px below the card's bottom border, so the content
       keeps ~1.2rem of clearance underneath and never sits behind them. */
    #it-main-parent-container .it-pause-top {
        display: flex; align-items: center; gap: 0.5rem; padding: 0.6rem 0.6rem 1.25rem; flex-wrap: wrap;
    }
    /* Accent disc with a black glyph — the same 20px badge the numbered timer
       cards carry, so a pause reads as a peer of the timers around it. */
    #it-main-parent-container .it-pause-glyph {
        flex-shrink: 0; width: 20px; height: 20px; display: flex; align-items: center; justify-content: center;
        border-radius: 50%; background: var(--color-accent, #ffd84b); color: #000;
    }
    #it-main-parent-container .it-pause-title { flex: 0 0 auto; font-size: 13px; color: var(--color-accent); }
    /* The first card also carries the lead insert bar on its top border, so a
       pause in that slot needs the same clearance above as it has below. */
    #it-main-parent-container #it-timers > .it-timer--pause:first-child .it-pause-top { padding-top: 1.25rem; }
    #it-main-parent-container .it-pause-top .it-remove { margin-left: auto; }
    #it-main-parent-container .it-timer-head {
        display: flex; align-items: center; justify-content: space-between; gap: 0.75rem;
        padding: 0.75rem; border-bottom: 1px solid var(--border-accent-light);
    }
    /* Fixed-size: the volume control is the only thing that may resize, so the title
       and its H:M:S boxes must never be shrunk out from under the preview button. */
    #it-main-parent-container .it-timer-head-left { display: flex; align-items: center; gap: 0.75rem; flex: 0 0 auto; min-width: 0; }
    /* .it-timer-name — shared style defined in CSS/style.css */
    /* Duration boxes reuse the pause cards' .it-pause-hms / .it-pause-colon styling
       (incl. its light-theme rules) — only the row's own gap is set here. */
    #it-main-parent-container .it-duration { display: flex; align-items: center; gap: 0.25rem; }
    /* Header duration quick-picks — the only chips left (the pause cards' quick-picks
       are gone; a gap's length is typed in its H:M:S boxes). Natural width on a
       single line: the pills and the ✕ are the header's fixed right-hand end, and the
       volume control beside them is what takes up the slack. */
    #it-main-parent-container .it-dur-chips { display: flex; align-items: center; gap: 0.3rem; flex: 0 0 auto; flex-wrap: nowrap; }

    /* Timer remove — grey circle with a centered dark X (SVG), red with white X on hover */
    #it-main-parent-container .it-remove {
        width: 26px; height: 26px; flex-shrink: 0; border-radius: 50%; border: 0; cursor: pointer; padding: 0;
        background: rgba(211,211,211,1) url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' width='24' height='24'%3E%3Cpath d='M18 6L6 18M6 6l12 12' stroke='%23141414' stroke-width='2.5' stroke-linecap='round'/%3E%3C/svg%3E") center/13px no-repeat;
        transition: all .2s ease;
    }
    #it-main-parent-container .it-remove:hover:not(:disabled) {
        background: var(--color-danger) url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' width='24' height='24'%3E%3Cpath d='M18 6L6 18M6 6l12 12' stroke='%23FFFFFF' stroke-width='2.5' stroke-linecap='round'/%3E%3C/svg%3E") center/13px no-repeat;
    }
    #it-main-parent-container .it-remove:disabled { opacity: 0.3; cursor: not-allowed; }

    /* Row remove — grey circle with a centered dark X (SVG, so it's geometrically
       centered at this small size), red with white X on hover. Matches .it-remove. */
    #it-main-parent-container .it-row-remove {
        width: 20px; height: 20px; flex-shrink: 0; border-radius: 50%; border: 0; cursor: pointer; padding: 0;
        background: rgba(211,211,211,1) url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' width='24' height='24'%3E%3Cpath d='M18 6L6 18M6 6l12 12' stroke='%23141414' stroke-width='2.5' stroke-linecap='round'/%3E%3C/svg%3E") center/10px no-repeat;
        transition: all .2s ease;
    }
    #it-main-parent-container .it-row-remove:hover:not(:disabled) {
        background: var(--color-danger) url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' width='24' height='24'%3E%3Cpath d='M18 6L6 18M6 6l12 12' stroke='%23FFFFFF' stroke-width='2.5' stroke-linecap='round'/%3E%3C/svg%3E") center/10px no-repeat;
    }
    #it-main-parent-container .it-row-remove:disabled { opacity: 0.3; cursor: not-allowed; }

    /* Timer volume — sits in the head row between the time inputs and close button */
    /* The header's one elastic element — it grows to close the gap between the preview
       button and the pills. The basis must be 0, not auto: the shared
       .bws-volume-control sets width:100%, so an auto basis asks for the whole header
       and the resulting shrink is taken out of the title group instead. border-box
       because .fsb-row-volume-control is content-box with .75rem side padding, which
       otherwise spills 24px past whatever width flex resolves for it. */
    #it-main-parent-container .it-timer-head-vol { box-sizing: border-box; flex: 1 1 0%; min-width: 0; margin-top: 0; }

    /* Timer preview — small round accent play button in front of the volume slider.
       Holds both glyphs; .it-preview-on swaps play for stop. */
    #it-main-parent-container .it-preview {
        width: 26px; height: 26px; flex-shrink: 0; padding: 0; border: 0; border-radius: 50%;
        display: flex; align-items: center; justify-content: center; cursor: pointer;
        background: #d9b53c; color: #111;
        transition: background-color .2s ease;
    }
    #it-main-parent-container .it-preview:hover:not(:disabled) { background: var(--color-accent, #ffd84b); }
    #it-main-parent-container .it-preview:disabled { opacity: 0.3; cursor: not-allowed; }
    #it-main-parent-container .it-preview .it-preview-play { margin-left: 1px; }   /* optically centre the triangle */
    #it-main-parent-container .it-preview .it-preview-stop { display: none; }
    /* Playing: red with the stop square, no ring — the colour change is the whole
       signal, matching the motionless hover everywhere else. Same darker-at-rest /
       lighter-on-hover pair as the yellow state, in red. */
    #it-main-parent-container .it-preview.it-preview-on { background: #d32f2f; color: #fff; }
    #it-main-parent-container .it-preview.it-preview-on:hover:not(:disabled) { background: #f44336; }
    #it-main-parent-container .it-preview.it-preview-on .it-preview-play { display: none; }
    #it-main-parent-container .it-preview.it-preview-on .it-preview-stop { display: block; }

    /* Isochronic tone/pulse layer rows */
    /* The yellow add/duplicate pair sits centred on each row card's bottom border;
       the gap and bottom padding are what keep it clear of the next row and of the
       green add-timer pair on the card's own bottom border. */
    #it-main-parent-container .it-rows { padding: 0.75rem 0.75rem 3.25rem; display: flex; flex-direction: column; gap: 1.75rem; }
    /* Padding is even on all four sides so the tone sub-row's volume cell runs the
       full width up to the card's inner right edge. The X needs no reserved
       gutter: it is absolutely placed at the top-right, over the beat sub-row,
       whose fn / wave / volume columns are empty. */
       #it-main-parent-container .it-row {
        position: relative;
        border: 1px solid var(--border-accent-light); border-radius: 6px;
        /* The yellow add/duplicate pair straddles this card's bottom border and
           reaches 13px above it, so the bottom padding has to clear that disc —
           same 20px Frequency Timers gives its rows. */
        padding: 0.6rem 0.6rem 20px; background: rgba(255,255,255,0.02);
    }
    /* Badge (row number) sits to the left of both preset dropdowns, vertically centered */
    #it-main-parent-container .it-row-main { display: flex; align-items: center; gap: 0.6rem; }
    #it-main-parent-container .it-badge {
        width: 26px; height: 26px; border-radius: 4px; background: var(--color-accent);
        color: #111; font-weight: 600; font-size: 12px; display: flex; align-items: center; justify-content: center; flex-shrink: 0;
    }
    #it-main-parent-container .it-subrows { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 0.5rem; }

    /* Both sub-rows share one column template so preset + custom align between
       beat and carrier. Carrier fills all columns; beat leaves fn/wave/vol empty. */
    /* The wave column is the gear button's own 34px, not the 152px the old
       four-toggle group needed, so the volume cell starts right after the gear
       instead of across a dead gap. It stays a fixed track (not auto) because
       each sub-row is its own grid: an auto column would collapse to 0 on the
       beat row, where the wave cell is absent, and pull its fr columns out of
       line with the tone row's. */
    /* preset · custom Hz · function · wave gear · row preview · volume */
    #it-main-parent-container .it-subrow {
        display: grid;
        grid-template-columns: minmax(120px, 1.15fr) minmax(70px, 0.55fr) minmax(96px, 0.62fr) 34px 26px minmax(120px, 1.4fr);
        gap: 0.5rem; align-items: center;
    }
    #it-main-parent-container .it-row .bbp-frequency-input { width: 100%; }
    #it-main-parent-container .it-row .bws-dropdown { width: 100%; }
    #it-main-parent-container .it-subrow .bws-volume-control { flex: 1; }
    /* Row volume cell — the shared .fsb-row-volume-control is written for a wrapped,
       full-width row: it carries a top margin and a content-box width: 100% whose
       .75rem side padding then spills 24px past its grid track. Inside this grid it
       has to fill its track exactly and centre with the rest of the tone sub-row.
       (The X sits absolutely at the card's top-right, so nothing collides here.) */
    #it-main-parent-container .it-subrow .it-vol-cell {
        box-sizing: border-box;
        margin-top: 0;
        flex-basis: auto;
    }

    /* "No Sound" beat, or beat not yet set: dim + disable the carrier sub-row (beat row stays active). */
    #it-main-parent-container .it-row-nosound .it-subrow-carrier,
    #it-main-parent-container .it-row-beatless .it-subrow-carrier { opacity: 0.35; pointer-events: none; }

    /* Tone not yet populated: dim fn / waveform / volume in the tone sub-row. */
    #it-main-parent-container .it-subrow-carrier.it-carrier-nosound .it-fn-cell,
    #it-main-parent-container .it-subrow-carrier.it-carrier-nosound .it-wave-cell,
    #it-main-parent-container .it-subrow-carrier.it-carrier-nosound .it-vol-cell { opacity: 0.35; pointer-events: none; }

    /* Remove button — top-right corner of the row card */
    #it-main-parent-container .it-row > .it-row-remove { position: absolute; top: 6px; right: 6px; }

    /* Manual Hz field — match dropdown height; gold left-aligned "Custom..." placeholder */
    #it-main-parent-container .it-hz-input {
        height: 38px; text-align: left; padding: 0.625rem 0.5rem; font-size: 13px; font-variant-numeric: tabular-nums;
    }
    #it-main-parent-container .it-hz-input::placeholder { color: var(--color-accent); opacity: 0.85; }
    #it-main-parent-container .it-hz-input::-moz-placeholder { color: var(--color-accent); opacity: 0.85; }

    /* Preset trigger — looks like .bws-dropdown but holds a left-aligned label + caret */
    #it-main-parent-container .it-preset-trigger {
        display: flex; align-items: center; text-align: left; cursor: pointer;
        color: var(--text-muted);
    }
    #it-main-parent-container .it-preset-trigger.has-value { color: var(--color-accent); }
    #it-main-parent-container .it-preset-trigger .it-preset-trigger-text {
        flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
    }

    /* Preset dropdown popup: styled by the shared block near .hpp-preset-dropdown. */
    /* Field-level preset info button (to the right of the pulse / tone trigger) */
    #it-main-parent-container .it-preset-cell { display: flex; align-items: center; gap: 0.4rem; }
    #it-main-parent-container .it-preset-cell .it-preset-trigger { flex: 1; min-width: 0; }
    /* look + disabled state come from .bws-info-badge */

    /* ── Preset info modal (mirrors mobile FrequencyInfoModal) ── */
    .it-info-modal .it-im-title { flex: 1; color: var(--text-primary, #fff); font-size: 16px; font-weight: 700; }
    .it-info-modal .it-im-close {
        width: 28px; height: 28px; flex-shrink: 0; border-radius: 50%; border: 0; cursor: pointer;
        background: rgba(211,211,211,1); color: rgba(20,20,20,0.95); font-size: 12px; line-height: 1;
        display: flex; align-items: center; justify-content: center;
    }
    .it-info-modal .it-im-close:hover { background: var(--color-danger); color: #fff; }
    .it-info-modal .it-im-underline { height: 1px; background: rgba(128,128,128,0.5); margin: 0 1rem; }
    .it-info-modal .it-im-image { width: 100%; aspect-ratio: 1 / 1; object-fit: contain; border-radius: 6px; display: block; background: rgba(255,255,255,0.03); }
    .it-info-modal .it-im-top { display: grid; grid-template-columns: 260px 1fr; gap: 16px; margin-bottom: 0; align-items: start; }
    .it-info-modal .it-im-top.no-image { grid-template-columns: 1fr; }
    .it-info-modal .it-im-main { min-width: 0; }
    /* Lower section: left col = Play Preview + Details + Usage; right col = Therapeutic + Safety */
    .it-info-modal .it-im-lower {
        margin-top: 14px; padding-top: 14px; border-top: 1px solid rgba(255,216,75,0.12);
        display: grid; grid-template-columns: 1fr 1fr; gap: 10px; align-items: start;
    }
    .it-info-modal .it-im-col { display: flex; flex-direction: column; gap: 10px; min-width: 0; }
    .it-info-modal .it-im-col .it-im-details,
    .it-info-modal .it-im-col .it-im-section,
    .it-info-modal .it-im-col .it-im-preview-row { margin: 0; }
    .it-info-modal .it-im-preview-btn { display: flex; width: 100%; align-items: center; justify-content: center; gap: 0.4rem; }
    .it-info-modal .it-im-preview-btn.it-preview-on { background: var(--color-danger, #e0564a); border-color: var(--color-danger, #e0564a); color: #fff; }
    .it-info-modal .it-im-set-btn { width: 100%; margin-top: 16px; font-size: 14px; font-weight: 500; }
    @media (max-width: 600px) {
        .it-info-modal .it-im-top { grid-template-columns: 1fr; }
        .it-info-modal .it-im-lower { grid-template-columns: 1fr; }
    }
    .it-info-modal .it-im-hz { color: var(--color-accent); font-size: 22px; font-weight: 700; margin-bottom: 2px; }
    .it-info-modal .it-im-subtitle { color: rgba(248,249,250,0.9); font-size: 14px; font-weight: 500; margin-bottom: 12px; }
    .it-info-modal .it-im-desc { color: rgba(248,249,250,0.85); font-size: 13px; line-height: 20px; margin-bottom: 14px; }
    .it-info-modal .it-im-details {
        margin-bottom: 10px; padding: 0.5rem 0.625rem; border-radius: 4px;
        background: rgba(255,216,75,0.05); border: 1px solid rgba(255,216,75,0.12);
    }
    .it-info-modal .it-im-detail { color: rgba(248,249,250,0.8); font-size: 12px; line-height: 20px; }
    .it-info-modal .it-im-detail-label { color: var(--color-accent); font-weight: 600; }
    .it-info-modal .it-im-section {
        margin-top: 10px; padding: 0.625rem; border-radius: 4px; background: rgba(255,255,255,0.03);
    }
    .it-info-modal .it-im-section-title {
        color: var(--color-accent); font-size: 13px; font-weight: 600; margin-bottom: 8px;
        padding-bottom: 4px; border-bottom: 1px solid rgba(255,216,75,0.2);
    }
    .it-info-modal .it-im-section-item { color: rgba(248,249,250,0.8); font-size: 12px; line-height: 18px; margin-bottom: 4px; }

    /* Waveform toggle buttons (4 in a row, same look as Frequency Session Builder) */
    #it-main-parent-container .it-wave-toggles { display: flex; gap: 0.3rem; }
    #it-main-parent-container .it-wave-toggles .fsb-row-wave-button { width: 34px; height: 34px; }

    /* Binaural Enhance toggle — a plain square icon button after the custom pulse input,
       same look/states as the waveform toggle buttons (gold outline; filled/glowing when active). */
    /* The Enhance group holds five controls, so it spans every track after the pulse
       preset + Hz pair (which stay in columns 1-2, aligned with the tone sub-row above).
       nowrap: the whole beat sub-row is one line by design — the fields shrink rather
       than dropping to a second row.
       The right padding clears the row's ✕, which is absolutely positioned at the card's
       top-right (20px wide at right:6px) and so sits over the end of this, the first
       sub-row. Without it the gear lands underneath the ✕. */
    #it-main-parent-container .it-subrow .it-be-cell {
        grid-column: 3 / -1; justify-self: stretch; min-width: 0;
        display: flex; align-items: center; flex-wrap: nowrap; gap: 0.4rem;
        padding-right: 30px;
    }
    #it-main-parent-container .it-be-toggle {
        display: flex; align-items: center; justify-content: center;
        width: 38px; height: 38px; padding: 2px;
        border: 1px solid var(--border-accent-light); border-radius: 4px;
        background: var(--bg-dark-light); color: var(--text-muted);
        cursor: pointer; transition: all .2s ease;
    }
    #it-main-parent-container .it-be-toggle:hover {
        background: var(--bg-accent-light); border-color: var(--color-accent);
        color: var(--text-bright); box-shadow: 0 0 6px var(--border-accent-light);
    }
    #it-main-parent-container .it-be-toggle.active {
        background: var(--border-accent-light); border-color: var(--color-accent);
        color: var(--color-accent); box-shadow: 0 0 8px var(--glow-accent);
    }
    #it-main-parent-container .it-be-toggle svg { width: 20px; height: 20px; flex-shrink: 0; }

    @media (max-width: 760px) {
        #it-main-parent-container .it-subrow-beat {
            grid-template-columns: 1fr 1fr;
            grid-template-areas: "sel sel" "hz hz" "be be";
            row-gap: 0.4rem;
        }
        /* Narrow screens get the one line back as a wrapped block — five controls cannot
           share a phone-width row without shrinking the fields past readable. */
        #it-main-parent-container .it-subrow-beat .it-be-cell {
            grid-area: be; justify-self: stretch; flex-wrap: wrap;
        }
        #it-main-parent-container .it-bb-cell { flex: 1 1 100%; }
        /* The gear and the row-preview button are content-sized; the volume box takes
           the rest of the last line. */
        #it-main-parent-container .it-subrow-carrier {
            grid-template-columns: auto auto 1fr;
            grid-template-areas: "sel sel sel" "hz hz fn" "wave prev vol";
            row-gap: 0.4rem;
        }
        #it-main-parent-container .it-subrow .it-preset-cell { grid-area: sel; }
        #it-main-parent-container .it-subrow .it-hz-cell { grid-area: hz; }
        #it-main-parent-container .it-subrow .it-fn-cell { grid-area: fn; }
        #it-main-parent-container .it-subrow .it-wave-cell { grid-area: wave; justify-self: start; }
        #it-main-parent-container .it-subrow .it-row-preview { grid-area: prev; justify-self: start; }
        #it-main-parent-container .it-subrow .it-vol-cell { grid-area: vol; }
    }

/* ───── from MEDIA-LIBRARY-MODULE/index.html ───── */
/* Auth notice */
    .mlib-auth-notice {
        display: flex;
        align-items: center;
        gap: 0.6rem;
        padding: 0.75rem 1rem;
        margin-bottom: 0.5rem;
        background: var(--bg-dark-medium, rgba(255,255,255,0.04));
        border: 1px solid rgba(255, 152, 0, 0.3);
        border-radius: 4px;
        font-size: 13px;
        color: rgba(255, 152, 0, 0.9);
    }
    .mlib-auth-notice strong { color: var(--color-accent, #ffd84b); }
    #mlib-content-inner { transition: opacity 0.2s; }

    /* ── Section tabs (segmented, like the frequency-presets tabs) ───────── */
    .mlib-tab {
        flex: 1; display: inline-flex; align-items: center; justify-content: center; gap: 0.4rem;
        padding: 0.6rem 1rem; border-radius: 8px; cursor: pointer;
        background: var(--bg-dark-lighter, #1a1a1a); color: var(--text-muted, #aaa);
        border: 1px solid var(--border-accent-light, rgba(255,216,75,0.25));
        font-size: 14px; font-weight: 500; font-family: inherit;
        transition: background .2s ease, color .2s ease, border-color .2s ease;
    }
    .mlib-tab:hover { border-color: var(--color-accent, #ffd84b); color: var(--color-accent, #ffd84b); }
    .mlib-tab.active {
        background: var(--color-accent, #ffd84b); color: #111;
        border-color: var(--color-accent, #ffd84b);
    }
    /* Refresh button shares the filter-bar button look */
    .mlib-fbar-refresh { display: inline-flex; align-items: center; gap: 0.35rem; }

    /* ── Filter / search bar ─────────────────────────────────────────────── */
    .mlib-fbar {
        display: flex;
        flex-wrap: wrap;
        align-items: flex-end;
        gap: 10px;
        width: 100%;
        box-sizing: border-box;
        padding: 12px;
        margin-top: 0.6rem;
        border-radius: 8px;
        background: var(--bg-dark-lighter, #1a1a1a);
        border: 1px solid var(--border-accent-light, rgba(255,216,75,0.25));
    }
    .mlib-fbar-field { display: flex; flex-direction: column; gap: 4px; flex: 1; min-width: 150px; }
    .mlib-fbar-field--range { flex: 1.4; min-width: 210px; }
    .mlib-fbar-field label {
        font-size: 10px; font-weight: 700; letter-spacing: 0.06em;
        text-transform: uppercase; color: var(--text-muted, #aaa);
    }
    .mlib-fbar-input {
        width: 100%; box-sizing: border-box; padding: 8px 9px; border-radius: 5px;
        border: 1px solid var(--border-accent-light, rgba(255,216,75,0.25));
        background: var(--bg-dark-light, #141414); color: #fff;
        font-size: 13px; font-family: inherit;
    }
    .mlib-fbar-input:focus { outline: none; border-color: var(--color-accent, #ffd84b); }
    .mlib-fbar-input:disabled,
    .mr-save-go:disabled { opacity: 0.45; cursor: not-allowed; }
    .mlib-fbar-rangewrap { display: flex; align-items: center; gap: 6px; }
    .mlib-fbar-num { -moz-appearance: textfield; }
    .mlib-fbar-dash { color: var(--text-muted, #888); flex: 0 0 auto; }
    .mlib-fbar-clear {
        flex: 0 0 auto; padding: 8px 14px; border-radius: 5px; cursor: pointer;
        font-size: 12px; font-weight: 700;
        border: 1px solid var(--border-accent-light, rgba(255,216,75,0.25));
        background: var(--bg-dark-light, #141414); color: var(--text-muted, #aaa);
    }
    .mlib-fbar-clear:hover { border-color: var(--color-accent, #ffd84b); color: var(--color-accent, #ffd84b); }

    /* List status */
    .mlib-list-status {
        padding: 1.25rem 1rem;
        text-align: center;
        font-size: 13px;
        border-radius: 4px;
        margin: 0.5rem 0;
    }
    .mlib-status-loading { color: var(--color-accent, #ffd84b); }
    .mlib-status-empty { color: var(--text-muted, rgba(255,255,255,0.4)); font-style: italic; }
    .mlib-status-error { color: var(--color-danger, #f44336); }
    /* Saved-playlist results share #mlib-list-status with the track list, so both
       states are explicit modifiers rather than a default. --warn covers "loaded,
       but some tracks were skipped": a result, not an error. */
       .mlib-list-status--ok { color: var(--text-bright, rgba(255,255,255,0.85)); }
    .mlib-list-status--warn {
        color: var(--color-accent, #ffd84b);
        border: 1px solid var(--border-accent-light, rgba(255,216,75,0.25));
        background: rgba(255,216,75,0.06);
    }
    /* Card covers are fetched authenticated and injected into the placeholder box,
       which already carries the 120px sizing. */
    .plp-pcard-cover > img { width: 100%; height: 100%; object-fit: cover; display: block; }

    /* ── Save-playlist dialog: total time + the rows as they will be saved ────
       Lives inside .fp-save-dialog (shared save-dialog shell), below the cover
       section. Scrolls on its own so a long playlist cannot push Save off. */
    .mlib-cs-summary {
        border: 1px solid var(--border-accent-light, rgba(255,216,75,0.25));
        border-radius: 6px;
        background: var(--bg-dark-lighter, #1a1a1a);
        margin: 4px 0 12px;
        overflow: hidden;
    }
    .mlib-cs-summary-head {
        display: flex; align-items: center; justify-content: space-between; gap: 12px;
        padding: 8px 12px;
        border-bottom: 1px solid var(--border-accent-light, rgba(255,216,75,0.25));
        font-size: 12px; font-weight: 600;
        color: var(--text-muted, #aaa); text-transform: uppercase; letter-spacing: 0.3px;
    }
    .mlib-cs-total { color: var(--color-accent, #ffd84b); }
    .mlib-cs-tracks { list-style: none; margin: 0; padding: 0; max-height: 220px; overflow-y: auto; }
    .mlib-cs-row {
        display: flex; align-items: center; gap: 10px;
        padding: 6px 12px;
        font-size: 13px; color: var(--text-bright, rgba(255,255,255,0.85));
    }
    .mlib-cs-row + .mlib-cs-row { border-top: 1px solid rgba(255,255,255,0.06); }
    .mlib-cs-row--pause { color: var(--text-muted, #888); font-style: italic; }
    .mlib-cs-num { flex: 0 0 22px; text-align: right; color: var(--text-muted, #888); font-size: 12px; }
    .mlib-cs-title { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
    .mlib-cs-badge {
        margin-left: 6px; padding: 1px 5px; border-radius: 3px;
        font-size: 10px; font-style: normal; font-weight: 600; text-transform: uppercase; letter-spacing: 0.3px;
        color: var(--color-accent, #ffd84b); border: 1px solid var(--border-accent-light, rgba(255,216,75,0.25));
    }
    .mlib-cs-dur { flex: 0 0 auto; font-variant-numeric: tabular-nums; color: var(--text-muted, #aaa); font-size: 12px; }

    /* Track list */
    @media (max-width: 900px) { .mlib-track-list { grid-template-columns: repeat(2, 1fr); } }
    @media (max-width: 600px) { .mlib-track-list { grid-template-columns: 1fr; } }
    .mlib-empty-state {
        grid-column: 1 / -1;
        padding: 2rem 1rem;
        text-align: center;
        color: var(--text-muted, rgba(255,255,255,0.4));
        font-size: 13px;
        font-style: italic;
    }

    /* Pager */
    .mlib-pager:empty { display: none; }
    .mlib-pager button {
        padding: 6px 14px; border-radius: 6px; cursor: pointer; font-weight: 600; font-family: inherit;
        background: var(--bg-dark-lighter, #1a1a1a);
        border: 1px solid var(--border-accent-light, rgba(255,216,75,0.25));
        color: var(--color-accent, #ffd84b);
    }
    .mlib-pager button:hover:not(:disabled) { border-color: var(--color-accent, #ffd84b); }
    .mlib-pager button:disabled { opacity: 0.3; cursor: not-allowed; }
    .mlib-pager-info { font-size: 13px; color: var(--text-muted, #aaa); }

    /* ── Track card (image-left layout) ──────────────────────────────────── */
    /* cursor: pointer because the whole card opens the track's info modal, the
       same gesture .plp-pcard carries in every other grid — the inner buttons
       are <button>s and keep their own cursor. */
    .mlib-track-card {
        cursor: pointer;
        display: flex;
        align-items: stretch;
        background: var(--bg-dark-light, #141414);
        border: 1px solid var(--border-accent-light, rgba(255,216,75,0.22));
        border-radius: 8px;
        overflow: hidden;
        position: relative;
        transition: border-color 0.15s, box-shadow 0.15s;
    }
    .mlib-track-card:hover {
        border-color: var(--color-accent, #ffd84b);
        box-shadow: 0 0 8px var(--glow-accent, rgba(255,216,75,0.18));
    }
    /* In-playlist: emphasised yellow border (matches the playing-track style) */
    .mlib-track-card--routed {
        border-color: var(--color-accent, #ffd84b);
        box-shadow: 0 0 0 1px var(--color-accent, #ffd84b), 0 0 14px rgba(255,216,75,0.30);
    }

    .mlib-rc-cover {
        width: 96px; flex: 0 0 96px; object-fit: cover;
        background: #111; align-self: stretch; display: block;
    }
    .mlib-rc-cover-ph {
        display: flex; align-items: center; justify-content: center;
        color: rgba(255,216,75,0.35);
    }
    .mlib-rc-body {
        flex: 1; min-width: 0; padding: 11px 13px;
        display: flex; flex-direction: column; gap: 6px; position: relative;
    }
    .mlib-rc-info {
        position: absolute; top: 9px; right: 11px;
    }
    .mlib-rc-title {
        font-size: 15px; font-weight: 700; color: var(--text-primary, #fff);
        line-height: 1.25;
        overflow: hidden; text-overflow: ellipsis;
        display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical;
    }
    .mlib-rc-title-wrap { display: flex; align-items: flex-start; gap: 0.3rem; padding-right: 26px; }
    .mlib-rc-labels { display: flex; flex-wrap: wrap; gap: 7px; }
    /* Boosted / Extended on a Boosted/Extended card: same row as the
       duration and date pills, pushed to the card's right edge. */
    .mlib-rc-labels .mlib-rc-kind { margin-left: auto; align-self: center; white-space: nowrap; }
    .mlib-rc-desc {
        font-size: 12px; color: var(--text-muted, #aaa); line-height: 1.4;
        display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden;
    }
    .mlib-rc-actions { display: flex; align-items: center; gap: 8px; margin-top: auto; padding-top: 4px; }
    .mlib-rc-playlist-btn {
        flex: 1; display: inline-flex; align-items: center; justify-content: center; gap: 6px;
        padding: 8px 10px; border-radius: 6px; cursor: pointer;
        font-size: 12px; font-weight: 700; font-family: inherit;
        border: 1px solid var(--color-accent, #ffd84b);
        background: var(--color-accent, #ffd84b); color: #000;
    }
    /* Long label in the large (cover-on-top) view, short one in compact — the
       default, so it also applies before BWSCardView has set the attribute. */
    .mlib-pl-lbl-long { display: none; }
    #mlib-main-parent-container[data-bws-cardview="top"] .mlib-pl-lbl-long  { display: inline; }
    #mlib-main-parent-container[data-bws-cardview="top"] .mlib-pl-lbl-short { display: none; }
    /* When already in the playlist → outlined "Remove" state */
    .mlib-rc-playlist-btn.routed {
        background: transparent; color: var(--color-accent, #ffd84b);
    }
    /* "Boost" (Recorded / Uploaded cards) or "Extend" (Boosted cards) beside
       it: the same box and type as
       Add to playlist, in the preview button's two golds — darker at rest,
       the accent on hover. */
    .mlib-rc-boost-btn {
        flex: 0 0 auto; display: inline-flex; align-items: center; justify-content: center; gap: 4px;
        padding: 8px 14px; border-radius: 6px; cursor: pointer;
        font-size: 12px; font-weight: 700; font-family: inherit;
        border: 1px solid #d9b53c; background: #d9b53c; color: #111;
        transition: background-color .2s ease, border-color .2s ease;
    }
    /* The 15px credit bolt; negative margin keeps it from making the button
       taller than Add to playlist beside it. */
    .mlib-rc-boost-btn svg { flex-shrink: 0; margin: -2px 0; }
    /* Compact cards (the default view): Boost and Extend are the icon alone,
       so the row has room. The button keeps its aria-label and tooltip. */
    #mlib-main-parent-container:not([data-bws-cardview="top"]) .mlib-rc-boost-btn { padding: 8px 10px; }
    #mlib-main-parent-container:not([data-bws-cardview="top"]) .mlib-rc-boost-btn .mlib-rc-ai-text { display: none; }
    .mlib-rc-boost-btn:hover {
        background: var(--color-accent, #ffd84b); border-color: var(--color-accent, #ffd84b);
    }
    .mlib-rc-del {
        flex: 0 0 auto; padding: 8px 11px; border-radius: 6px; cursor: pointer;
        font-size: 12px; font-weight: 600; display: inline-flex; align-items: center;
        border: 1px solid var(--border-accent-light, rgba(255,216,75,0.25));
        background: var(--bg-dark-lighter, #1a1a1a); color: var(--color-accent, #ffd84b);
    }
    .mlib-rc-del:hover { background: #f44336; border-color: #f44336; color: #fff; }

    /* ── Uploaded Songs: entry card + file picker ─────────────────────────────
       The card pinned to the front of the Uploaded Songs grid gets the same
       treatment as the entry cards in the preset modules: the whole tile is one
       button, and its two yellow parts — the glyph cover and the action button —
       rest in a darker gold and brighten to the accent together on hover. The
       button is inert so it can never take the hover or the focus on its own. */
    .mlib-card-upload .bws-card-cover--custom {
        display: flex; align-items: center; justify-content: center; overflow: hidden;
        background: #d9b53c; transition: background-color .2s ease;
    }
    .mlib-card-upload .bws-card-create-btn {
        font-weight: 500; pointer-events: none;
        background: #d9b53c; border-color: #d9b53c;
        transition: background-color .2s ease, border-color .2s ease;
    }
    .mlib-card-upload:hover .bws-card-cover--custom,
    .mlib-card-upload:hover .bws-card-create-btn {
        background: var(--color-accent, #ffd84b); border-color: var(--color-accent, #ffd84b);
    }
    .mlib-card-upload:focus-visible {
        outline: 2px solid var(--color-accent, #ffd84b); outline-offset: 2px;
    }
    .mlib-card-upload .mlib-rc-desc { -webkit-line-clamp: 3; }

    /* File row in the upload dialog. The native input is replaced by the app's
       own button — a file input styles inconsistently across browsers, and the
       chosen file needs room to be named, sized and timed. */
    .mlib-tc-fileinput { display: none; }
    .mlib-tc-filerow {
        display: flex; align-items: center; gap: 10px; flex-wrap: wrap; margin-bottom: 4px;
    }
    .mlib-tc-filerow .prb-action-btn { flex: 0 0 auto; }
    .mlib-tc-filename {
        flex: 1; min-width: 0; font-size: 12px; color: var(--text-muted, #aaa);
        overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
    }
    .mlib-info-cover {
        display: block;
        flex-shrink: 0;
        width: 220px;
        max-width: 48%;
        aspect-ratio: 1 / 1;
        object-fit: cover;
        margin: 0;
        border-radius: 6px;
        border: 1px solid var(--color-accent, #ffd84b);
    }
    @media (max-width: 460px) {
        .mlib-info-cover { width: 100%; max-width: 240px; margin: 0 auto; }
    }

    /* ── Track info modal ─────────────────────────────────────────────────
       The shell is the shared preset modal (.fp-modal + .bws-modal-*), so the
       cover, title, pills, description and tags are styled once, over there,
       for every module. What is local to this one is the action row: preview,
       the AI action and download, with the inline player that replaces the
       preview button while a track is playing.

       This one carries a player and can grow, so it needs a height budget and a
       scrolling body rather than running off the screen — but it no longer sets
       those by hand: the height cap, flex column, non-shrinking cover and
       scrolling body all come from the cover-on-top info modal template (see
       .alb-modal), so nothing is left to add here. */
    /* Wraps because the player takes the preview button's place mid-row: at the
       modal's width the three controls and a player cannot share one line. */
    .mlib-im-playrow { display: flex; align-items: center; flex-wrap: wrap; gap: 10px; }
    /* "Boosted from: <song>" under a boosted song's title. Pulled up into the
       name row's bottom margin so it reads as part of the title block. */
    .mlib-im-source { margin: -4px 0 12px; font-size: 12px; line-height: 1.4; color: var(--color-accent, #ffd84b); }
    .mlib-im-source-link {
        padding: 0; border: 0; background: none; cursor: pointer;
        font: inherit; color: inherit; text-align: left;
        text-decoration: underline; text-underline-offset: 2px;
    }
    .mlib-im-source-link:hover { color: #fff; }
    .mlib-im-source-gone { font-style: italic; opacity: 0.75; }
    /* Preview and the AI button are the same gold control: neither one spends
       anything: Preview plays what is already there, and the AI button only
       opens the dialog where a generation is set up and paid for. The bolt the
       AI button carries says which dialog, not what it costs — it paints in
       currentColor, so it comes out black on the gold like the label.

       Darker gold at rest, accent gold under the cursor — the app's two-tone for
       a primary action, the same as .alb-modal-load, .plp-pcard-load and the
       entry cards' create button. */
    .mlib-im-preview-btn,
    .mlib-im-ai-btn {
        display: inline-flex; align-items: center; gap: 8px; padding: 9px 16px; border-radius: 6px;
        cursor: pointer; font-size: 13px; font-weight: 700; font-family: inherit;
        border: 1px solid #d9b53c; background: #d9b53c; color: #000;
        transition: background-color .2s ease, border-color .2s ease;
    }
    .mlib-im-preview-btn:hover:not(:disabled),
    .mlib-im-ai-btn:hover:not(:disabled) {
        background: var(--color-accent, #ffd84b); border-color: var(--color-accent, #ffd84b);
    }
    /* Preview goes disabled while it loads the audio, and the AI button had this
       from the violet dress it used to wear. */
    .mlib-im-preview-btn:disabled,
    .mlib-im-ai-btn:disabled { opacity: 0.55; cursor: default; }
    .mlib-im-player {
        flex: 1 1 220px; min-width: 0; display: flex; align-items: center; gap: 8px;
        padding: 7px 10px; border-radius: 6px; background: var(--bg-dark-light, #141414);
        border: 1px solid var(--border-accent-light, rgba(255,216,75,0.25));
    }
    .mlib-im-pbtn { background: none; border: none; color: var(--color-accent, #ffd84b); cursor: pointer; padding: 2px; display: inline-flex; flex: 0 0 auto; }
    .mlib-im-time { font-size: 11px; color: var(--text-muted, #aaa); font-variant-numeric: tabular-nums; flex: 0 0 auto; }
    .mlib-im-seek { flex: 1; min-width: 40px; }
    .mlib-im-volicon { color: var(--text-muted, #aaa); flex: 0 0 auto; }
    .mlib-im-vol { width: 72px; flex: 0 0 auto; }
    /* margin-left:auto - last control in the row, parked on the right edge, and
       it stays there when the player takes the preview button's place. */
    .mlib-im-dl {
        flex: 0 0 auto; margin-left: auto; width: 38px; height: 38px; border-radius: 6px; cursor: pointer;
        display: inline-flex; align-items: center; justify-content: center;
        border: 1px solid #2e7d32; background: #2e7d32; color: #fff;
    }
    .mlib-im-dl:hover { background: #388e3c; }
    /* ── AI options modal (wraps the enhance / boost / extend panels) ─────── */
    .mlib-ai-modal-overlay {
        position: fixed; inset: 0; z-index: 10001;
        display: none; align-items: flex-start; justify-content: center;
        padding: 4vh 16px; overflow-y: auto;
        /* The overlay is the only thing that scrolls while it is open: `contain`
           stops a wheel that reaches either end from chaining to the page
           behind, and the JS lock covers the case where the dialog is short
           enough not to scroll at all. -webkit- for iOS momentum scrolling. */
        overscroll-behavior: contain;
        -webkit-overflow-scrolling: touch;
    }
    .mlib-ai-modal-overlay.open { display: flex; }
    .mlib-ai-modal { width: 100%; max-width: 700px; margin: auto 0; }
    .mlib-ai-modal .mlib-enhance-panel { margin: 0; }

    /* Type badges */
    /* Track type, beside the title in the info modal. Same chip as the album and
       preset modals' access-tier badges; only the fill is the library's own -
       accent yellow for a track the user recorded, the AI purple for one Suno
       touched. The text keeps the accent colour rather than the family's white:
       yellow is what tells these two apart at a glance. */
    /* Upper-cased in CSS, not in the label, so the text stays readable in the
       markup and in a screen reader's ear. */
    .mlib-badge { text-transform: uppercase; }
    /* My Library's kind tags (Original / Uploaded / Boosted / Extended) - on the
       cards, in the info modal and on the playlist rows - are this one badge.
       After the shared .*-badge rule above, so it wins for these alone. */
    .mlib-badge { padding: 4px 6px; font-size: 8px; font-weight: 500; }
    .mlib-badge-orig { background: rgba(255,216,75,0.20); color: var(--color-accent, #ffd84b); }
    .mlib-badge-enh { background: rgba(147,112,219,0.28); color: #cdbcf2; }

    /* On a playlist row the tag sits in a one-line flex row, so it centres
       on that line and never squeezes or wraps. */
    .mlib-pl-kind { align-self: center; flex-shrink: 0; white-space: nowrap; }
    /* Every row's tag is as wide as the widest label it could hold, so the
       titles after it line up down the list. The width is measured, not set:
       the two longest labels sit invisibly in the same grid cell as the real
       one, so a change to the badge's font or size carries through. */
    .mlib-pl-kind { display: inline-grid; justify-items: center; }
    .mlib-pl-kind-text,
    .mlib-pl-kind::before,
    .mlib-pl-kind::after { grid-area: 1 / 1; }
    .mlib-pl-kind::before { content: 'Extended'; visibility: hidden; }
    .mlib-pl-kind::after  { content: 'Uploaded'; visibility: hidden; }

    /* Delete uses the shared red button style (.hpp-rec-record-btn); these rules
       size it to sit inline with the other action buttons. */
    .mlib-delete-btn {
        margin-left: 0;
        height: auto;
        padding: 0.3rem 0.55rem;
    }

    /* Custom player control bar — all controls live in one container */
    .mlib-play-btn:hover { background: rgba(255, 216, 75, 0.14); }
    /* Add-to-playlist toggle */
    .mlib-route-btn {
        flex-shrink: 0;
        display: flex;
        align-items: center;
        justify-content: center;
        width: 26px;
        height: 26px;
        padding: 0;
        border-radius: 4px;
        border: 1px solid var(--color-border, rgba(255,255,255,0.15));
        background: rgba(255,255,255,0.04);
        color: var(--text-muted, rgba(255,255,255,0.5));
        cursor: pointer;
        transition: all 0.15s;
    }
    .mlib-route-btn:hover {
        border-color: var(--color-accent, #ffd84b);
        color: var(--color-accent, #ffd84b);
    }
    .mlib-route-btn.routed {
        border-color: var(--color-accent, #ffd84b);
        background: rgba(255, 216, 75, 0.18);
        color: var(--color-accent, #ffd84b);
        box-shadow: 0 0 8px rgba(255, 216, 75, 0.45);
    }

    /* Armed card — accent ring + soft outer glow */
    .mlib-track-card--routed {
        border-color: var(--color-accent, #ffd84b) !important;
        box-shadow: 0 0 0 1px rgba(255, 216, 75, 0.5), 0 0 14px rgba(255, 216, 75, 0.25);
    }

    /* ── My Library Playlist segment ───────────────────────────────────────── */
    /* One-line status above the clip list: a track that could not load, an output
       that would not start, tracks an export had to leave out. Hidden until there
       is something to say (plNotice in MEDIA-LIBRARY-MODULE/js/media-library.js
       sets display). Mirrors .alb-pl-note in the album engine. */
    .mlib-pl-note { display: none; font-size: 12px; line-height: 1.35; color: var(--color-warning, #ff9800); background: rgba(255,152,0,0.08); border: 1px solid rgba(255,152,0,0.35); border-radius: 6px; padding: 0.5rem 0.65rem; margin-bottom: 0.5rem; }
    .mlib-pl-empty {
        font-size: 12px;
        color: var(--text-muted, rgba(255,255,255,0.4));
        padding: 0.85rem;
        text-align: center;
        border: 1px dashed var(--color-border, rgba(255,255,255,0.12));
        border-radius: 6px;
    }
    .mlib-pl-card {
        background: var(--bg-dark-medium, rgba(255,255,255,0.04));
        border: 1px solid var(--color-border, rgba(255,255,255,0.1));
        border-radius: 6px;
        overflow: hidden;
    }
    .mlib-pl-card.mlib-pl-dragging { opacity: 0.45; }
    .mlib-pl-card.mlib-pl-drop-above { box-shadow: inset 0 3px 0 0 var(--color-accent, #ffd84b); }
    .mlib-pl-card.mlib-pl-drop-below { box-shadow: inset 0 -3px 0 0 var(--color-accent, #ffd84b); }

    /* Drag-to-reorder, the same three beats as the site's section list: the row being
       carried lifts and dims, the row under the cursor leans away to show which side
       the drop will land on, and the row that landed flashes once so the eye can find
       it after the list redraws. Last word in the file on purpose — the two older
       copies of the dragging/drop rules above only set opacity and the edge lines. */
    .alb-pl-card,
    .mlib-pl-card { transition: transform .15s ease, box-shadow .15s ease; }
    .alb-pl-card.alb-pl-dragging,
    .mlib-pl-card.mlib-pl-dragging {
        opacity: 0.45; position: relative; z-index: 10;
        transform: scale(0.995); box-shadow: 0 12px 32px rgba(0,0,0,0.6);
    }
    .alb-pl-card.alb-pl-slide-up,
    .mlib-pl-card.mlib-pl-slide-up { transform: translateY(-10px); }
    .alb-pl-card.alb-pl-slide-down,
    .mlib-pl-card.mlib-pl-slide-down { transform: translateY(10px); }
    /* Out-specifies the playing row's breathing glow for half a second, then hands
       the border back to it. */
    @keyframes bwsPlCardLand {
        0%   { box-shadow: 0 0 0 2px rgba(255,216,75,0.75), 0 0 14px rgba(255,216,75,0.45); }
        100% { box-shadow: 0 0 0 0 rgba(255,216,75,0); }
    }
    .alb-pl-card.alb-pl-just-landed,
    .mlib-pl-card.mlib-pl-just-landed { animation: bwsPlCardLand 0.5s ease forwards; }
    @media (prefers-reduced-motion: reduce) {
        .alb-pl-card,
        .mlib-pl-card { transition: none; }
        .alb-pl-card.alb-pl-slide-up,
        .mlib-pl-card.mlib-pl-slide-up,
        .alb-pl-card.alb-pl-slide-down,
        .mlib-pl-card.mlib-pl-slide-down { transform: none; }
    }
    .mlib-pl-drag-handle {
        flex-shrink: 0;
        display: flex;
        align-items: center;
        padding: 0 0.1rem;
        color: rgba(255, 216, 75, 0.35);
        cursor: grab;
        transition: color 0.15s;
    }
    .mlib-pl-drag-handle:hover { color: rgba(255, 216, 75, 0.85); }
    .mlib-pl-drag-handle:active { cursor: grabbing; }
    /* Playlist row player — the same control bar as a library card, but inline on
       the row (see .mlib-pl-top > .mlib-player), so it drops .mlib-player's accent
       border, radius and padding. */
    .mlib-pl-transport .mlib-pl-master-slider { flex: 1; min-width: 0; width: auto; }

    /* Seek + IN/OUT chevron range track (per-clip start/stop trim) */
    /* Passed-time fill clamped to the chevron window: track color before IN, accent only IN→position */
    .mlib-seek-col .mlib-seek { background: linear-gradient(to right, var(--slider-track-bg, rgba(255,255,255,0.15)) 0%, var(--slider-track-bg, rgba(255,255,255,0.15)) var(--in, 0%), var(--color-accent) var(--in, 0%), var(--color-accent) var(--val, 0%), var(--slider-track-bg, rgba(255,255,255,0.15)) var(--val, 0%)) !important; }
    .mlib-seek-handle:hover, .mlib-seek-handle:active { border-bottom-color: #ffd84b; }
    .mlib-trim-dur { color: #ffd84b !important; }
    @keyframes mlib-spin { to { transform: rotate(360deg); } }

    /* ── Duration/date pills — single source of truth ────────────────────────
       Every time and date pill in the app is this one rule: preset and album
       cards, playlist rows, info modals, the media library, the presets
       builder and the timer preset grids. There is no module-prefixed copy —
       modules emit .bws-meta-badge (+ .bws-badge-duration for the accent
       variant) and inherit whatever changes here. */
    .bws-meta-badge {
        font-size: 11.5px;
        font-weight: 600;
        color: var(--text-muted, rgba(255,255,255,0.45));
        padding: 0.18rem 0.5rem;
        background: var(--bg-dark-heavy, rgba(0,0,0,0.25));
        border: 1px solid var(--color-border, rgba(255,255,255,0.08));
        border-radius: 20px;
        white-space: nowrap;
        flex-shrink: 0;
        display: inline-flex;
        align-items: center;
        gap: 4px;
    }
    /* Accent variant — a duration reads as the card's headline number. Date,
       count and tag pills stay neutral. */
    .bws-badge-duration { border-color: rgba(255,216,75,0.25); color: var(--color-accent, #ffd84b); }
    /* Inherits the badge's accent color via stroke="currentColor". */
    .bws-badge-icon { width: 12.5px; height: 12.5px; flex-shrink: 0; }
    /* Pill row inside a preset card body. */
    .bws-card-labels { display: flex; flex-wrap: wrap; gap: 7px; margin-top: 2px; }
    /* One line under the card title: the duration/date pills first, then the
       tier badge (FREE / PAID / PERSONAL …) to their right. The pills arrive
       wrapped in their own .bws-card-labels div, which `display: contents`
       dissolves so every pill is a direct item of this row. */
    .bws-card-labelrow { display: flex; flex-wrap: wrap; align-items: center; gap: 7px; margin-top: 2px; }
    .bws-card-labelrow > .bws-card-labels,
    .bws-modal-pills > .bws-card-labels { display: contents; }
    .bws-card-labelrow > span { align-self: center; }

    /* ── Enhance accordion panel ──────────────────────────────────────────────── */
    /* Enhance / Extend / Boost all use this class; they only ever render inside
       .mlib-ai-modal now, so the fill comes from the shared glass rules. */
    .mlib-enhance-panel {
        border: 1px solid var(--color-accent); border-radius: 6px;
        padding: 1rem 1rem 0.75rem; box-shadow: 0 0 16px var(--glow-accent);
    }

    .mlib-enhance-header {
        display: flex;
        align-items: center;
        justify-content: space-between;
        margin-bottom: 0.85rem;
        padding-bottom: 0.6rem;
        border-bottom: 1px solid var(--color-border, rgba(255,255,255,0.1));
    }
    .mlib-enhance-title {
        display: flex;
        align-items: center;
        gap: 0.5rem;
        font-size: 13px;
        font-weight: 600;
        color: var(--color-accent, #ffd84b);
    }
    .mlib-close-btn {
        background: none;
        border: none;
        cursor: pointer;
        color: var(--text-muted, rgba(255,255,255,0.4));
        padding: 0.2rem;
        display: flex;
        align-items: center;
        transition: color 0.15s;
    }
    .mlib-close-btn:hover { color: var(--color-danger, #f44336); }

    /* Mode tabs */
    .mlib-mode-row {
        display: flex;
        align-items: center;
        gap: 0.5rem;
        margin-bottom: 0.85rem;
        flex-wrap: wrap;
    }
    .mlib-mode-btn {
        min-width: 80px;
        opacity: 0.55;
        transition: opacity 0.15s;
    }
    .mlib-mode-btn.active {
        opacity: 1;
        background: rgba(255,216,75,0.12) !important;
        border-color: var(--color-accent, #ffd84b) !important;
        color: var(--color-accent, #ffd84b) !important;
        box-shadow: 0 0 6px rgba(255,216,75,0.3);
    }
    /* .bws-group-description — shared style defined in CSS/style.css */

    /* Fields */
    .mlib-field-label {
        font-size: 11px;
        color: var(--text-muted, rgba(255,255,255,0.5));
        text-transform: uppercase;
        letter-spacing: 0.06em;
        font-weight: 600;
    }
    .mlib-textarea {
        width: 100%;
        resize: vertical;
        min-height: 65px;
        line-height: 1.5;
        font-family: inherit;
        box-sizing: border-box;
    }

    /* Toggle */
    .mlib-toggle-wrap input[type="checkbox"] { display: none; }
    .mlib-toggle-track {
        width: 36px;
        height: 20px;
        border-radius: 10px;
        background: var(--color-border, rgba(255,255,255,0.15));
        position: relative;
        flex-shrink: 0;
        transition: background 0.2s;
    }
    .mlib-toggle-track::after {
        content: '';
        position: absolute;
        left: 2px;
        top: 2px;
        width: 16px;
        height: 16px;
        border-radius: 50%;
        background: #fff;
        transition: left 0.2s;
    }
    .mlib-toggle-wrap input:checked + .mlib-toggle-track { background: var(--color-accent, #ffd84b); }
    .mlib-toggle-wrap input:checked + .mlib-toggle-track::after { left: 18px; }
    .mlib-toggle-text { font-size: 12px; color: var(--color-text, rgba(255,255,255,0.85)); }

    /* Advanced section */
    .mlib-advanced-header { margin-bottom: 0.4rem; }
    .mlib-text-btn {
        background: none;
        border: none;
        color: var(--text-muted, rgba(255,255,255,0.45));
        font-size: 12px;
        cursor: pointer;
        padding: 0.25rem 0;
        display: flex;
        align-items: center;
        gap: 0.35rem;
        transition: color 0.15s;
        font-family: inherit;
    }
    .mlib-text-btn:hover { color: var(--color-accent, #ffd84b); }
    .mlib-advanced-panel {
        background: rgba(0,0,0,0.2);
        border: 1px solid var(--color-border, rgba(255,255,255,0.1));
        border-radius: 4px;
        padding: 0.85rem;
        margin-bottom: 0.5rem;
    }
    .mlib-slider-group { display: flex; flex-direction: column; gap: 0.35rem; margin-bottom: 0.85rem; }
    .mlib-slider-label {
        font-size: 12px;
        color: var(--text-muted, rgba(255,255,255,0.5));
        min-width: 100px;
        text-align: right;
    }
    .mlib-param-slider {
        flex: 1;
        height: 4px;
        cursor: pointer;
        accent-color: var(--color-accent, #ffd84b);
    }
    .mlib-slider-val {
        font-size: 12px;
        color: var(--color-accent, #ffd84b);
        min-width: 36px;
        text-align: right;
        font-variant-numeric: tabular-nums;
    }

    /* Generate row */
    .mlib-generate-row {
        justify-content: space-between;
        align-items: center;
        flex-wrap: wrap;
        gap: 0.75rem;
        margin-top: 0.25rem;
        padding-top: 0.6rem;
        border-top: 1px solid var(--color-border, rgba(255,255,255,0.1));
    }
    .mlib-generate-btn {
        display: flex;
        align-items: center;
        gap: 0.5rem;
        padding: 0.55rem 1.2rem;
        font-size: 13px;
        font-weight: 600;
        min-width: 180px;
        justify-content: center;
    }
    .mlib-btn-generating {
        animation: mlib-pulse 1.4s ease-in-out infinite;
        cursor: not-allowed;
        opacity: 0.8;
    }
    @keyframes mlib-pulse {
        0%, 100% { box-shadow: 0 0 6px rgba(255,216,75,0.3); }
        50% { box-shadow: 0 0 18px rgba(255,216,75,0.6); }
    }

    /* Status row */
    .mlib-spinner {
        width: 14px;
        height: 14px;
        border: 2px solid rgba(255,216,75,0.2);
        border-top-color: var(--color-accent, #ffd84b);
        border-radius: 50%;
        animation: mlib-spin 0.7s linear infinite;
        flex-shrink: 0;
    }
    .mlib-status-text {
        font-size: 12px;
        white-space: nowrap;
        overflow: hidden;
        text-overflow: ellipsis;
    }
    .mlib-status-loading { color: var(--color-accent, #ffd84b); }
    .mlib-status-success { color: var(--color-success, #4caf50); }
    .mlib-status-error { color: var(--color-danger, #f44336); }
    .mlib-status-info { color: var(--text-muted, rgba(255,255,255,0.5)); }
    .mlib-elapsed {
        font-family: 'Courier New', monospace;
        font-size: 13px;
        font-weight: 600;
        color: var(--color-accent, #ffd84b);
        min-width: 38px;
        text-align: right;
    }

    /* Results */
    .mlib-results-label {
        display: flex;
        align-items: center;
        gap: 0.4rem;
        margin: 0.85rem 0 0.6rem;
        font-size: 12px;
        color: var(--text-muted, rgba(255,255,255,0.5));
    }
    /* The 60-minute window on generated TTS audio, stated above the results.
       A deadline, not an error: warm rather than red, and it sits inside the
       results block so it disappears with them. */
    .gm-ai-expiry-note {
        margin: 0 0 0.6rem;
        padding: 0.5rem 0.7rem;
        border: 1px solid var(--border-accent-light, rgba(255,216,75,0.35));
        border-radius: 4px;
        background: rgba(255, 216, 75, 0.08);
        color: var(--text-secondary, #ddd);
        font-size: 12px;
        line-height: 1.45;
    }
    .gm-ai-expiry-note strong { color: var(--color-accent, #ffd84b); }
    .mlib-results-container {
        display: flex;
        flex-direction: column;
        gap: 0.6rem;
        padding-bottom: 0.25rem;
    }

    /* Result card (generated Suno output) */
    .mlib-result-card {
        background: var(--bg-dark-medium, rgba(255,255,255,0.04));
        border: 1px solid rgba(255,216,75,0.15);
        border-radius: 5px;
        overflow: hidden;
    }
    .mlib-result-header {
        display: flex;
        align-items: center;
        gap: 0.65rem;
        padding: 0.6rem 0.75rem;
    }
    .mlib-result-thumb {
        width: 46px;
        height: 46px;
        border-radius: 4px;
        object-fit: cover;
        flex-shrink: 0;
        background: rgba(0,0,0,0.3);
    }
    .mlib-result-thumb-placeholder {
        width: 46px;
        height: 46px;
        border-radius: 4px;
        background: rgba(0,0,0,0.3);
        border: 1px solid var(--color-border, rgba(255,255,255,0.1));
        display: flex;
        align-items: center;
        justify-content: center;
        flex-shrink: 0;
        color: var(--text-muted, rgba(255,255,255,0.3));
    }
    .mlib-result-meta {
        flex: 1;
        min-width: 0;
        display: flex;
        flex-direction: column;
        gap: 0.3rem;
    }
    .mlib-result-title {
        font-size: 13px;
        font-weight: 600;
        color: var(--color-text, rgba(255,255,255,0.9));
        white-space: nowrap;
        overflow: hidden;
        text-overflow: ellipsis;
    }
    .mlib-result-tags {
        display: flex;
        flex-wrap: wrap;
        gap: 0.3rem;
    }
    /* The description under a generated variation. Prose, not metadata: it wraps
       and stays plain, where .mlib-result-tags beside it holds the real badges
       (duration, date). Clamped to three lines so a long description cannot push
       the audition player off the bottom of a result card. */
    .mlib-result-desc {
        margin-top: 0.3rem;
        font-size: 11px;
        line-height: 1.45;
        color: var(--text-bright, rgba(255,255,255,0.72));
        display: -webkit-box;
        -webkit-line-clamp: 3;
        -webkit-box-orient: vertical;
        overflow: hidden;
    }
    .mlib-result-btns {
        display: flex;
        gap: 0.4rem;
        flex-shrink: 0;
    }
    .mlib-save-btn {
        display: flex;
        align-items: center;
        gap: 0.3rem;
        font-size: 11px;
        padding: 0.25rem 0.6rem;
        white-space: nowrap;
    }
    .mlib-result-audio {
        width: 100%;
        height: 34px;
        display: block;
        background: var(--bg-dark-heavy, rgba(0,0,0,0.3));
        border-top: 1px solid var(--color-border, rgba(255,255,255,0.08));
        outline: none;
        accent-color: var(--color-accent, #ffd84b);
    }
    .mlib-no-audio {
        padding: 0.4rem 0.75rem;
        font-size: 11px;
        color: var(--text-muted, rgba(255,255,255,0.4));
        border-top: 1px solid var(--color-border, rgba(255,255,255,0.08));
        font-style: italic;
    }

/* ───── from MEDIA-RECORDER-MODULE-PRO/index.html ───── */
/* Channel rows: toggle button on the left, volume strip to its right */
    .mr-modules-row {
        display: flex;
        flex-direction: column;
        gap: 0.5rem;
        margin-bottom: 1.5rem;
        padding: 0 15px;
    }

    /* Per-channel Solo / Mute — square toggle stack in front of each control bar.
       Design mirrors the shamanic-drum segment buttons (.sdp-seg-copy/reset-btn). */
    .mr-sm-btn {
        display: inline-flex;
        align-items: center;
        justify-content: center;
        width: 20px;
        height: 20px;
        border-radius: 3px;
        border: 1px solid rgba(255, 216, 75, 0.4);
        background: #1a1608;
        color: rgba(255, 216, 75, 0.7);
        font-family: inherit;
        font-size: 12px;
        font-weight: 500;
        line-height: 1;
        cursor: pointer;
        padding: 0;
        flex-shrink: 0;
        transition: color 0.15s, background 0.15s, border-color 0.15s;
    }
    .mr-sm-btn:hover {
        color: var(--color-accent, #ffd84b);
        background: #26200b;
        border-color: rgba(255, 216, 75, 0.5);
    }
    /* Solo active — bright accent fill */
    .mr-solo-btn.is-active {
        color: #0c0c0c;
        background: var(--color-accent, #ffd84b);
        border-color: var(--color-accent, #ffd84b);
    }
    /* Mute active — red fill, mirrors .sdp-seg-reset-btn */
    .mr-mute-btn.is-active {
        color: #000;
        background: #b3261e;
        border-color: rgba(220, 60, 60, 0.7);
    }
    .mr-mute-btn.is-active:hover {
        background: #dc3c3c;
        border-color: #dc3c3c;
    }

    /* Transport panel — master bar + action buttons grouped as one unit */
    .mr-transport-panel {
        margin: 0.5rem 0 1rem;
        padding: 0.85rem 1rem;
        border: 1px solid var(--border-accent-light);
        border-radius: 10px;
        background: var(--bg-dark-light, rgba(255,255,255,0.03));
    }

    /* Master transport bar */
    .mr-master-bar {
        display: flex;
        align-items: center;
        gap: 1rem;
        padding: 0;
        margin: 0;
    }
    .mr-master-bar .bws-label { flex: 0 0 auto; margin: 0; }
    .mr-master-bar .hpp-player { flex: 1; min-width: 0; }
    /* Preview sits to the right of the transport it drives — the same shape the
       frequency-timer row uses for its Play button. */
    .mr-master-bar .bws-button-a { flex: 0 0 auto; margin-top: 0; }
    .mr-player-panel { margin-bottom: 0.6rem; }
    /* The console is the first row of its own panel now, so the rule that used to
       divide it from the transport above would draw a line under nothing. */
    .mr-rec-panel .mr-console { margin-top: 0; padding-top: 0; border-top: none; }
    @media (max-width: 620px) {
        .mr-master-bar { flex-wrap: wrap; }
        .mr-master-bar .bws-button-a { flex: 1 1 100%; }
    }

    /* ── Console — one action row ──────────────────────────────────────────
       Recorder source, that source's settings, and the button that starts the
       take. Every field carries a label above it, so the row aligns on the
       field baseline rather than on the middle of the boxes. */
    .mr-console {
        display: flex;
        align-items: flex-end;
        flex-wrap: wrap;
        gap: 10px;
        margin: 0.85rem 0 0;
        padding-top: 0.85rem;
        border-top: 1px solid var(--border-accent-light);
    }
    .mr-console-spacer { flex: 1 1 auto; }
    /* Icon + label, the same glyphs the transport play button uses. */
    .mr-play-btn { display: inline-flex; align-items: center; justify-content: center; gap: 8px; }
    .mr-play-icon, .mr-pause-icon { flex: 0 0 auto; }
    .mr-console-btn { flex: 0 0 auto; white-space: nowrap; text-align: center; }
    /* A console button the recorder has taken out of play — Play Session while a
       take owns the sources, the record button while a take is finalizing. The
       element is already disabled, but .bws-button-a has no disabled state of its
       own: it stayed fully lit and still went bright yellow on hover, which reads
       as "press me" for a click that is refused. The hover selector is repeated
       because :hover still matches a disabled button. */
    .mr-console-btn:disabled,
    .mr-console-btn:disabled:hover {
        opacity: .4;
        cursor: not-allowed;
        background: #d9b53c;
        border-color: #d9b53c;
        color: #111;
    }
    /* One fixed width across all four labels — sized to the longest, "Play & Record
       Video + Audio" — so neither the source nor the run state moves the row. The
       side padding is trimmed from the .bws-button-a 2rem to keep that width sane. */
    .mr-console-rec {
        box-sizing: border-box;
        width: 250px; min-width: 250px; max-width: 250px;
        padding-left: 1.25rem; padding-right: 1.25rem;
        display: inline-flex; align-items: center; justify-content: center; gap: 8px;
    }
    /* Record dot — the universal "this take is being captured" mark. Solid white
       in both themes so it reads as a lamp rather than as themed text. */
    .mr-rec-mark { flex: 0 0 auto; fill: #fff; }
    /* Reads as the same control as the header's Stop & Save (.mr-rec-stop): a flat
       red at rest, brightening to solid on hover. Overrides the translucent
       .bws-button-a-v2 default, which only reached this red on hover. */
    .mr-console-rec.bws-button-a-v2 {
        background: rgba(220,53,69,.7) !important;
        border-color: #dc3545 !important;
        color: #fff !important;
        box-shadow: none !important;
        transition: background-color .2s ease;
    }
    .mr-console-rec.bws-button-a-v2:hover {
        background: #dc3545 !important;
        box-shadow: none !important;
    }
    /* Recorder source — the field the rest of the row answers to, so it leads and
       keeps one width whichever source is picked. Sized to "Video + Audio", the
       longer of the two, so the row never reflows on a change of source. */
    .mr-src-field {
        flex: 0 0 150px;
        box-sizing: border-box;
        width: 150px; min-width: 150px; max-width: 150px;
    }
    .mr-console .mr-out-mode {
        box-sizing: border-box;
        width: 100%; min-width: 0; max-width: 100%;
    }
    /* The settings for the selected source take the middle of the row; the spacer
       after them pushes the record button to the right edge. */
    .mr-console .mr-video-opts { flex: 1 1 auto; min-width: 0; margin: 0; }
    .mr-console .mr-video-opt { flex: 1 1 130px; min-width: 0; }
    .mr-console .mr-video-opt-fps { flex: 0 0 78px; }
    .mr-console .mr-video-opt-abr { flex: 0 0 150px; }
    /* ── Record chain wears the record red ────────────────────────────────
       Every field in this row describes the take that the red record button
       starts, so the row carries its colour instead of the app's yellow accent.
       Done by re-pointing the accent variables on the row: every rule underneath
       (.bws-dropdown, the custom select popup, .bbp-frequency-input) already
       reads them, so nothing has to be restated per element. */
    .mr-rec-panel .mr-console {
        --color-accent:         #dc3545;
        --color-accent-dark:    #b02a37;
        --bg-accent-heavy:      rgba(220, 53, 69, 0.35);
        --bg-accent-medium:     rgba(220, 53, 69, 0.20);
        --bg-accent-light:      rgba(220, 53, 69, 0.10);
        --border-accent-light:  rgba(220, 53, 69, 0.30);
        --border-accent-medium: rgba(220, 53, 69, 0.22);
        --glow-accent:          rgba(220, 53, 69, 0.30);
        --glow-accent-medium:   rgba(220, 53, 69, 0.25);
        --glow-accent-heavy:    rgba(220, 53, 69, 0.50);
        --glow-accent-strong:   rgba(220, 53, 69, 0.60);
    }
    /* The chevron is a baked-in yellow data-URI on .bws-dropdown, so it is the one
       thing the variables cannot reach — same path, red stroke. */
    .mr-rec-panel .mr-console .bws-dropdown {
        background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23dc3545' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M6 9l6 6 6-6'%3E%3C/path%3E%3C/svg%3E");
    }

    /* Red is the frame, not the content: the values themselves stay white in every
       state (rest, hover, focus, and the selected row the closed select echoes), so
       a setting is always read as text rather than as another accent. */
    .mr-rec-panel .mr-console .bws-dropdown,
    .mr-rec-panel .mr-console .bws-dropdown:hover,
    .mr-rec-panel .mr-console .bws-dropdown:focus,
    .mr-rec-panel .mr-console .bbp-frequency-input,
    .mr-rec-panel .mr-console .bbp-frequency-input:hover,
    .mr-rec-panel .mr-console .bbp-frequency-input:focus,
    .mr-rec-panel .mr-console .bws-dropdown option:checked {
        color: #fff !important;
    }
    /* Settings describe the take, so they are inert while one runs. */
    .mr-rec-panel .mr-console .bws-dropdown:disabled,
    .mr-rec-panel .mr-console .bbp-frequency-input:disabled {
        opacity: 0.4; cursor: not-allowed;
    }

    .mr-video-status { flex: 0 0 auto; font-size: 11.5px; color: var(--text-muted, #aaa); min-width: 0; }

    /* Output settings — the half of the console that follows the recorder source. */
    .mr-video-opts {
        display: flex;
        align-items: flex-end;
        flex-wrap: wrap;
        gap: 12px;
    }
    /* Fields share the settings half evenly; everything below 520px stacks. */
    .mr-video-opt { flex: 1 1 calc(50% - 6px); min-width: 0; }
    .mr-video-opt > span { display: block; margin-bottom: 4px; font-size: 11px; font-weight: 700; letter-spacing: 0.04em; text-transform: uppercase; color: var(--text-muted, #aaa); }
    .mr-video-opt .bws-dropdown { width: 100%; }
    @media (max-width: 520px) {
        .mr-video-opt { flex: 1 1 100%; }
    }
    /* Each custom size field carries its own label, so the pair aligns on the field
       edge like every other labelled field in the row; the × sits with them. */
    .mr-video-custom { display: inline-flex; align-items: flex-end; gap: 6px; flex: 0 0 auto; }
    .mr-console .mr-video-opt-num { flex: 0 0 auto; }
    /* .bbp-frequency-input defaults to 34px tall, 11px type, right-aligned — all
       three matched to the selects beside it, so the custom size fields read as the
       same kind of field: same edges, same type size, text starting at the left. */
    .mr-video-num {
        width: 104px; height: 38px;
        padding: 0 10px;
        font-size: 13px;
        text-align: left;
    }
    .mr-video-x { color: var(--text-muted, #888); padding-bottom: 10px; }
    .mr-out-note {
        flex: 1 1 100%; margin: 2px 0 0;
        font-size: 11px; line-height: 1.45; color: var(--text-muted, #aaa);
    }

    /* Narrow: the buttons take the full width rather than wrapping raggedly. */
    @media (max-width: 620px) {
        .mr-console-spacer { display: none; }
        .mr-console-btn { flex: 1 1 160px; }
        .mr-console-rec { flex: 0 0 250px; }
    }

    /* ── Save recording modal ───────────────────────────────────────────────── */
    .mr-modal-overlay {
        position: fixed; inset: 0; z-index: 10002;
        display: flex; align-items: flex-start; justify-content: center;
        padding: 5vh 16px; overflow-y: auto;
    }
    .mr-save-dialog {
        position: relative;
        width: 100%; max-width: 420px; margin: auto 0;
        border: 1px solid var(--color-accent, #ffd84b);
        border-radius: 8px; padding: 18px; box-shadow: 0 0 16px var(--glow-accent, rgba(255,216,75,0.4));
    }
    .mr-save-close {
        position: absolute; top: 12px; right: 12px;
        width: 28px; height: 28px; border-radius: 50%; border: 0; cursor: pointer;
        background: #f44336; color: #fff; font-size: 12px; line-height: 1;
        display: flex; align-items: center; justify-content: center;
    }
    .mr-save-close:hover { background: #e33e3e; }
    .mr-save-dialog h3 { margin: 0 0 14px; padding-right: 32px; font-size: 15px; color: var(--color-accent, #ffd84b); }
    .mr-save-dialog label { display: block; font-size: 12px; color: var(--text-muted, #aaa); margin: 0 0 4px; }
    /* :not(.mr-tags-input) — the tags input sits inside the bordered .mr-tags-field
       box and must stay borderless. Without the exclusion this rule outranks the
       .mr-tags-input rule below and draws a second box inside the field. */
    .mr-save-dialog input[type=text]:not(.mr-tags-input), .mr-save-dialog textarea {
        width: 100%; box-sizing: border-box; padding: 9px; margin-bottom: 12px; border-radius: 5px;
        border: 1px solid var(--border-accent-light, rgba(255,216,75,0.25));
        background: var(--bg-dark-light, #141414); color: #fff; font-size: 13px; font-family: inherit;
    }
    .mr-tags-field {
        display: flex; flex-wrap: wrap; align-items: center; gap: 6px; box-sizing: border-box;
        width: 100%; padding: 7px 8px; margin-bottom: 12px; border-radius: 5px;
        border: 1px solid var(--border-accent-light, rgba(255,216,75,0.25)); background: var(--bg-dark-light, #141414);
    }
    /* Borderless and transparent — the box around it is .mr-tags-field. Scoped to
       the dialog like the frequency/binaural/isochronic tag inputs. */
    .mr-save-dialog .mr-tags-input { flex: 1; min-width: 130px; width: auto; box-sizing: border-box; padding: 4px 2px; margin: 0; border: 0; background: transparent; color: #fff; font-size: 13px; font-family: inherit; }
    .mr-save-dialog .mr-tags-input:focus { outline: none; }
    .mr-tag-badge {
        display: inline-flex; align-items: center; gap: 4px; font-size: 11px; font-weight: 700;
        padding: 0.16rem 0.2rem 0.16rem 0.5rem; border-radius: 20px;
        background: rgba(255,216,75,0.10); border: 1px solid rgba(255,216,75,0.3); color: var(--color-accent, #ffd84b);
    }
    .mr-tag-remove { background: none; border: 0; color: inherit; cursor: pointer; font-size: 14px; line-height: 1; padding: 0 3px; }
    .mr-tag-remove:hover { color: #fff; }

    /* What the saved file will be — format, quality and size as pills, with a
       Change… that re-decides them here. The take is stored as raw float, so this
       is a lossless re-encode, not a re-record. */
    .mr-save-fileline { display: flex; flex-wrap: wrap; align-items: center; gap: 7px; margin-bottom: 14px; }
    /* Same accent button as Save to Library Only (.mr-save-cloud) — just sized to
       sit in the pill row rather than filling the dialog's action row. */
    .mr-save-change {
        margin-left: auto; padding: 8px 14px; border-radius: 6px; cursor: pointer;
        font-size: 13px; font-weight: 700; font-family: inherit; white-space: nowrap;
        border: 1px solid var(--color-accent, #ffd84b);
        background: var(--color-accent, #ffd84b); color: #000;
        transition: background-color .2s ease;
    }
    /* Stays yellow in every state — the open editor below is what shows it is
       active, so the button must not read as a different (white) control. */
    .mr-save-change:hover,
    .mr-save-change.open { background: var(--color-accent, #ffd84b); }
    .mr-save-fmt-edit {
        margin: -6px 0 14px;
        padding: 12px;
        border-radius: 6px;
        border: 1px solid var(--border-accent-light, rgba(255,216,75,0.25));
        background: var(--bg-dark-light, #141414);
    }

    .mr-dest-row { display: flex; flex-direction: column; gap: 8px; margin-bottom: 14px; }
    .mr-save-dialog .mr-dest {
        display: flex; align-items: center; gap: 10px; cursor: pointer; margin: 0;
        padding: 9px 11px; border-radius: 6px; font-size: 13px; color: #fff;
        border: 1px solid var(--border-accent-light, rgba(255,216,75,0.25)); background: var(--bg-dark-light, #141414);
    }
    .mr-dest input { accent-color: var(--color-accent, #ffd84b); margin: 0; flex: 0 0 auto; }
    .mr-dest input:disabled { cursor: not-allowed; }
    .mr-dest:has(input:disabled) { opacity: 0.55; cursor: not-allowed; }
    /* Separated from the destination label by a rule, like the quality note in
       the gear panel — a secondary aside, not part of the label. */
    .mr-dest-note {
        margin-left: auto; padding-left: 10px; font-size: 11px; color: var(--text-muted, #aaa); font-style: italic;
        border-left: 1px solid var(--border-accent-light, rgba(255,216,75,0.25));
    }

    /* Storage meter in the save dialog. One track, two segments: solid accent for
       what the library already holds, a lighter block for what this take would
       add. The remainder of the track is the free space, so the whole bar reads
       as used / incoming / free at a glance. Colour is a warning, not decoration —
       amber past 80% of the allowance, red when the take does not fit. */
    .mr-storage { margin: -4px 0 14px; }
    .mr-storage-bar {
        display: flex; height: 6px; margin-bottom: 6px; overflow: hidden;
        border-radius: 3px; background: rgba(255,255,255,0.09);
    }
    .mr-storage-bar > i { display: block; height: 100%; transition: width .35s ease; }
    .mr-storage-used { min-width: 2px; background: var(--color-accent, #ffd84b); }
    .mr-storage-add { background: rgba(255,216,75,0.45); }
    .mr-storage-meter[data-level="warn"] .mr-storage-used { background: #ff9800; }
    .mr-storage-meter[data-level="warn"] .mr-storage-add { background: rgba(255,152,0,0.45); }
    .mr-storage-meter[data-level="full"] .mr-storage-used { background: #ef5350; }
    .mr-storage-line {
        display: flex; align-items: baseline; justify-content: space-between; gap: 10px;
        font-size: 11px; color: var(--text-muted, #aaa);
    }
    .mr-storage-line strong { font-weight: 700; color: #fff; }
    .mr-storage-warn {
        margin: 8px 0 0; padding: 7px 9px; border-radius: 5px;
        font-size: 11.5px; line-height: 1.5; color: #fff; background: rgba(239,83,80,0.14);
    }
    @media (prefers-reduced-motion: reduce) { .mr-storage-bar > i { transition: none; } }

    /* ── Recording banner (top bar) ────────────────────────────────────────
       Centred in the header for the whole take, so the state is visible from any
       module and the stop is always one click away. Absolutely positioned rather
       than placed in the header's flex row: it stays centred on the viewport
       regardless of how wide the brand or the signed-in user block happen to be. */
    .mr-rec-banner {
        position: absolute; left: 50%; top: 50%; transform: translate(-50%, -50%);
        z-index: 2;
        display: flex; align-items: center; gap: 10px;
        padding: 4px 4px 4px 12px; border-radius: 20px;
        background: rgba(239,83,80,0.12);
        border: 1px solid rgba(239,83,80,0.55);
        box-shadow: 0 0 12px rgba(239,83,80,0.25);
    }
    /* Standalone module page — no header to sit in. */
    .mr-rec-banner--float {
        position: fixed; top: 10px; transform: translateX(-50%); z-index: 1200;
        background: rgba(30,20,20,0.97);
    }
    .mr-rec-dot {
        width: 9px; height: 9px; flex: 0 0 auto; border-radius: 50%;
        background: #ef5350; box-shadow: 0 0 8px rgba(239,83,80,0.9);
        animation: mr-rec-blink 1.1s steps(1, end) infinite;
    }
    @keyframes mr-rec-blink { 0%, 49% { opacity: 1; } 50%, 100% { opacity: 0.15; } }
    .mr-rec-label {
        font-size: 12.5px; font-weight: 700; letter-spacing: 0.04em;
        color: #fff; white-space: nowrap;
    }
    .mr-rec-stop {
        flex: 0 0 auto; padding: 5px 12px; border-radius: 20px; cursor: pointer;
        font-size: 12px; font-weight: 600; font-family: inherit; white-space: nowrap;
        color: #fff;
        /* Same red as the recorder's own Stop & Save on hover (.bws-button-a-v2:hover),
           so the two controls for the same action look like the same thing. */
        border: 1px solid #dc3545; background: rgba(220,53,69,.7);
        box-shadow: inset 0 0 15px var(--glow-danger);
        transition: background-color .2s ease;
    }
    .mr-rec-stop:hover { background: #dc3545; }
    .mr-rec-stop:disabled { opacity: 0.6; cursor: default; }
    /* Narrow headers: the dot and the stop button carry the meaning on their own. */
    @media (max-width: 780px) { .mr-rec-label { display: none; } }
    @media (prefers-reduced-motion: reduce) { .mr-rec-dot { animation: none; } }

    /* ── "Cannot play the mix yet" ─────────────────────────────────────────
       One notice for the whole mix, listing every enabled module that has nothing
       to play, with the two ways out per module: go and fix it, or drop it from
       the mix. Uses the save dialog's shell so it reads as the same family. */
    .mr-block-lead { margin: 0 0 14px; font-size: 12.5px; line-height: 1.55; color: var(--text-bright, rgba(255,255,255,0.8)); }
    .mr-block-item {
        margin-bottom: 10px; padding: 12px;
        border-radius: 6px;
        border: 1px solid var(--border-accent-light, rgba(255,216,75,0.25));
        background: var(--bg-dark-light, #141414);
    }
    .mr-block-item:last-child { margin-bottom: 0; }
    .mr-block-name { font-size: 13px; font-weight: 700; color: var(--color-accent, #ffd84b); }
    .mr-block-rows {
        margin: 8px 0 0; padding-left: 18px;
        font-size: 12px; line-height: 1.6; color: var(--text-muted, #aaa);
    }
    .mr-block-actions { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 12px; }
    .mr-block-actions .bws-button-a { flex: 1 1 auto; }
    .mr-block-off {
        flex: 0 0 auto; padding: 8px 14px; border-radius: 6px; cursor: pointer;
        font-size: 12px; font-weight: 600; font-family: inherit; white-space: nowrap;
        border: 1px solid var(--border-accent-light, rgba(255,216,75,0.25));
        background: var(--bg-dark-lighter, #1a1a1a); color: var(--text-muted, #aaa);
        transition: border-color .2s ease, color .2s ease;
    }
    .mr-block-off:hover { border-color: var(--color-accent, #ffd84b); color: var(--color-accent, #ffd84b); }

    /* Crash-recovery prompt — the same dialog shell as the save modal, but it has
       no ✕ and cannot be dismissed by Esc or the backdrop: the take is unsaved
       work, so it leaves only by being recovered or explicitly discarded. */
    .mr-recover-dialog h3 { padding-right: 0; }
    .mr-recover-lead { margin: 0 0 14px; font-size: 12.5px; line-height: 1.55; color: var(--text-bright, rgba(255,255,255,0.8)); }
    .mr-recover-facts {
        margin-bottom: 16px; padding: 10px 12px; border-radius: 6px;
        border: 1px solid var(--border-accent-light, rgba(255,216,75,0.25));
        background: var(--bg-dark-light, #141414);
    }
    .mr-recover-facts > div { display: flex; justify-content: space-between; gap: 12px; padding: 3px 0; font-size: 12px; }
    .mr-recover-facts span { color: var(--text-muted, #aaa); }
    .mr-recover-facts strong { font-weight: 700; color: #fff; }
    .mr-recover-more { margin: 0 0 14px; font-size: 11.5px; color: var(--color-accent, #ffd84b); }
    .mr-recover-note { margin: 10px 0 0; font-size: 11px; text-align: center; color: var(--text-muted, #aaa); }
    .mr-recover-discard {
        flex: 0 0 auto; min-width: 90px;
        border: 1px solid rgba(239,83,80,0.5); background: rgba(239,83,80,0.12); color: #ef5350;
        transition: background-color .2s ease, color .2s ease;
    }
    .mr-recover-discard:hover { background: #ef5350; color: #fff; }

    .mr-save-dialog-row { display: flex; gap: 8px; flex-wrap: wrap; }
    .mr-save-dialog-row button { flex: 1; min-width: 120px; padding: 10px; border-radius: 6px; cursor: pointer; font-size: 13px; font-weight: 700; font-family: inherit; }
    .mr-save-cancel { flex: 0 0 auto; min-width: 80px; border: 1px solid var(--border-accent-light, rgba(255,216,75,0.25)); background: var(--bg-dark-lighter, #1a1a1a); color: var(--text-muted, #aaa); }
    .mr-save-cloud { border: 1px solid var(--color-accent, #ffd84b); background: var(--color-accent, #ffd84b); color: #000; }
    .mr-save-dialog-row button:disabled { opacity: 0.45; cursor: not-allowed; }
    .mr-save-status { margin-top: 12px; font-size: 12px; min-height: 16px; color: var(--text-muted, #aaa); }

    .mr-module-col {
        display: flex;
        flex-direction: row;
        align-items: center;
        gap: 1rem;
        width: 100%;
        position: relative;
        transition: transform 0.2s cubic-bezier(0.25, 0.8, 0.25, 1), opacity 0.2s ease;
    }

    .mr-module-col.is-dragging {
        opacity: 0.45;
        transform: scale(0.985);
        transform-origin: left center;
    }

    /* Open a gap by pushing every channel below the drop point downward */
    .mr-module-col.mr-col-nudge-down { transform: translateY(10px); }

    .mr-module-toggle-button {
        min-height: 70px;
        width: 100%;
        box-sizing: border-box;
        background: #000 !important;
        display: flex;
        flex-direction: column;
        align-items: center;
        justify-content: center;
        gap: 0.4rem;
        position: relative;
        padding: 0.5rem 0.5rem 0.5rem;
        cursor: pointer;
    }

    .mr-module-toggle-button:hover,
    .mr-module-toggle-button.active {
        background: #000 !important;
    }

    /* Drag handle — top-left corner */
    .mr-module-toggle-button .bws-section-drag-handle {
        position: absolute;
        top: 4px;
        left: 4px;
        display: flex;
        align-items: center;
        padding: 0.25rem 0.4rem 0.25rem 0.1rem;
        color: rgba(255, 216, 75, 0.3);
        transition: color 0.2s ease;
        flex-shrink: 0;
        border-radius: 3px;
    }

    .mr-module-toggle-button .bws-section-drag-handle:hover { color: rgba(255, 216, 75, 0.85); }

    /* Turn-off button — top-right corner (mirrors the Module Control Bar power btn) */
    .mr-power-btn {
        position: absolute;
        top: 4px;
        right: 4px;
        background: none;
        border: none;
        padding: 3px;
        cursor: pointer;
        display: flex;
        align-items: center;
        justify-content: center;
        border-radius: 3px;
        color: rgba(255, 216, 75, 0.6);
        transition: color 0.2s ease;
        z-index: 2;
    }
    .mr-power-btn:hover { color: #ffd84b; }
    /* The power button is the in/out-of-mix toggle, so it carries the state: accent
       while the module is excluded (the way back in), quiet while it is included. */
    .mr-module-toggle-button:not(.active) .mr-power-btn { color: var(--color-accent, #ffd84b); }
    .mr-power-btn svg { stroke: currentColor !important; width: auto !important; height: auto !important; }

    /* Kill the global stroke override from style.css on handle circles */
    .mr-module-toggle-button .bws-section-drag-handle svg {
        stroke: none !important;
        width: auto !important;
        height: auto !important;
    }

    /* Icon images */
    .mr-cb-icon {
        display: block;
        width: 30px;
        height: 30px;
        object-fit: cover;
        border-radius: 4px;
        transition: opacity 0.2s ease;
    }

    /* Full color when active, dimmed otherwise */
    .mr-module-toggle-button.active .mr-cb-icon { opacity: 1;   }
    .mr-module-toggle-button:not(.active) .mr-cb-icon { opacity: 0.3; }

    /* Title label */
    .mr-cb-title {
        font-size: 10px;
        text-transform: uppercase;
        letter-spacing: 0.04em;
        text-align: center;
        font-family: 'Roboto', -apple-system, BlinkMacSystemFont, "Segoe UI", "Helvetica Neue", sans-serif;
    }

    /* Dim the whole control bar (and its solo/mute stack) when the toggle is off.
       Uses ~ (general sibling) because the S/M stack now sits between the toggle
       button and the control bar, breaking the old adjacent-sibling (+) match. */
    .mr-module-toggle-button:not(.active) ~ .hpp-player,
    .mr-module-toggle-button:not(.active) ~ .mr-channel-main,
    .mr-module-toggle-button:not(.active) ~ .mr-sm-stack {
        opacity: 0.55;
    }

    /* Channel main column (bar on top, effects row below) */
    .mr-channel-main { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 6px; }
    /* Effect settings modal (mirrors the Audio Visualizer Pro 2 dialog) */
    .mr-modal-overlay {
        position: fixed; inset: 0; z-index: 100000;
        display: flex; align-items: center; justify-content: center; padding: 24px;
    }
    .mr-modal {
        width: 100%; max-width: 380px;
        border: 1px solid var(--color-accent, #ffd84b); border-radius: 8px; overflow: hidden;
        box-shadow: 0 0 18px var(--glow-accent, rgba(255,216,75,0.35));
    }
    .mr-modal-body { padding: 16px 18px 18px; }
    .mr-modal-sub { font-size: 12px; color: var(--text-muted, rgba(255,255,255,0.55)); margin: 0 0 14px; }
    .mr-modal-switch-row { display: flex; align-items: center; justify-content: space-between; margin: 0 0 12px; }
    .mr-modal-switch-row label { font-size: 13px; color: rgba(255,255,255,0.85); }
    .mr-modal-switch {
        padding: 5px 14px; border-radius: 4px; font-size: 12px; font-weight: 700; cursor: pointer;
        background: transparent; color: var(--text-muted); border: 1px solid rgba(255,255,255,0.2);
    }
    .mr-modal-switch.on { background: var(--color-accent, #ffd84b); color: #111; border-color: var(--color-accent, #ffd84b); }
    .mr-modal .bws-label { display: flex; justify-content: space-between; font-size: 12px; margin: 12px 0 4px; }
    .mr-modal-val { color: var(--color-accent); font-variant-numeric: tabular-nums; }
    .mr-modal input[type="range"] { width: 100%; }
    .mr-modal-actions { display: flex; gap: 10px; margin-top: 18px; }

    /* Segmented position track (one bar per timer) — mirrors the Frequency Timers
       module. The seek input goes transparent so the overlaid segments show. */
    .mr-modules-row .ft-seek-wrap {
        position: relative;
        flex: 3;
        min-width: 0;
        display: flex;
        align-items: center;
    }
    .mr-modules-row .ft-seek-wrap .hpp-seek {
        flex: 1;
        width: auto;
        position: relative;
        z-index: 1;
        background: transparent !important;
    }
    .mr-modules-row .ft-seek-segs {
        position: absolute;
        left: 0;
        right: 0;
        top: 50%;
        height: 6px;
        transform: translateY(-50%);
        display: flex;
        align-items: center;
        gap: 1px;
        pointer-events: none;
    }
    /* A channel whose source has nothing to seek along (a looping composition, a
       session with no timers) greys its mirrored slider — the bars under it have to
       follow, or the channel reads as playable when it is not. Matches the rule the
       modules apply to their own tracks. */
    .mr-modules-row .hpp-seek.hpp-seek-disabled ~ .ft-seek-segs { opacity: 0.4; }
    /* Segments copied into the overlay are the same .bws-seg spans the source bar
       drew, so they need no rules of their own — only the legacy case where a span
       arrived without the shared class (a module still rendering its own markup). */
    .mr-modules-row .ft-seek-segs > span:not(.bws-seg) {
        flex: 1 1 0%;
        min-width: 2px;
        height: 6px;
        border-radius: 3px;
        background: linear-gradient(to right, var(--color-accent) 0%, var(--color-accent) var(--segfill, 0%), var(--slider-track-bg, rgba(255,255,255,0.15)) var(--segfill, 0%));
    }

/* ───── from MEDITATION-MUSIC-MODULE/index.html ───── */
/* Title bar */
    #medm-section-playlist { margin-top: 1.5rem; }

    /* ── Presets: tabs + grid (mirrors Frequency Presets) ─────────────────── */
    #medm-main-parent-container .alb-tab.active { background: var(--color-accent); color: #111; border-color: var(--color-accent); }

    @media (max-width: 900px) { #medm-main-parent-container .alb-grid { grid-template-columns: repeat(2, 1fr); } }
    @media (max-width: 600px) { #medm-main-parent-container .alb-grid { grid-template-columns: 1fr; } }

    #medm-main-parent-container .alb-card {
        position: relative; display: flex; gap: 10px; min-height: 160px; cursor: pointer; overflow: hidden;
        background: var(--bg-dark-lighter); border: 1px solid var(--border-accent-light); border-radius: 4px;
        transition: border-color .2s ease, box-shadow .2s ease;
    }
    #medm-main-parent-container .alb-card:hover { border-color: var(--color-accent); box-shadow: 0 0 8px var(--glow-accent); }
    #medm-main-parent-container .alb-card-cover { width: 84px; flex-shrink: 0; object-fit: cover; background: #111; align-self: stretch; }
    #medm-main-parent-container .alb-card-body { flex: 1; min-width: 0; padding: 10px 10px 10px 0; display: flex; flex-direction: column; gap: 4px; }
    #medm-main-parent-container .alb-card-top { display: flex; align-items: flex-start; gap: 6px; }
    #medm-main-parent-container .alb-card-title { flex: 1; font-size: 14px; font-weight: 600; color: var(--text-primary, #fff); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
    /* .alb-info-btn look comes from .bws-info-badge */
    #medm-main-parent-container .alb-card-desc { font-size: 12px; color: var(--text-bright, rgba(255,255,255,0.8)); line-height: 1.4; display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; }
    #medm-main-parent-container .alb-card-empty { border: 1px dashed var(--border-accent-light); border-radius: 4px; min-height: 160px; opacity: 0.22; }

    .alb-badge-free { background: rgba(75,200,100,0.25); }
    .alb-badge-basic { background: rgba(75,150,255,0.25); }
    .alb-badge-premium { background: rgba(180,75,255,0.25); }
    .alb-badge-paid { background: rgba(255,216,75,0.18); }

    #medm-main-parent-container .alb-pager { display: flex; align-items: center; justify-content: center; gap: 14px; margin-top: 1rem; }
    #medm-main-parent-container .alb-pager button { padding: 6px 14px; border-radius: 4px; cursor: pointer; font-weight: 600; background: var(--bg-dark-lighter); border: 1px solid var(--border-accent-light); color: var(--color-accent); }
    #medm-main-parent-container .alb-pager button:disabled { opacity: 0.3; cursor: not-allowed; }
    #medm-main-parent-container .alb-pager-info { font-size: 13px; color: var(--text-muted); }
    #medm-main-parent-container .alb-personal-empty,
    #medm-main-parent-container .alb-grid-msg { grid-column: 1 / -1; text-align: center; color: var(--text-muted); padding: 2rem 1rem; font-size: 14px; line-height: 1.6; }

    /* ── Album info modal (cover/title/badge/description + track list) ─────── */

    /* ── Playlist segment (mirrors My Library Playlist) ───────────────────── */
    .alb-pl-card.alb-pl-dragging { opacity: 0.45; }
    .alb-pl-card.alb-pl-drop-above { box-shadow: inset 0 3px 0 0 var(--color-accent, #ffd84b); }
    .alb-pl-card.alb-pl-drop-below { box-shadow: inset 0 -3px 0 0 var(--color-accent, #ffd84b); }
    .alb-pl-drag-handle:hover { color: rgba(255,216,75,0.85); }
    .alb-pl-drag-handle:active { cursor: grabbing; }

    /* Row player control bar */
    .alb-play-btn:hover { background: rgba(255,216,75,0.14); }
    /* Seek + IN/OUT chevron range track (per-clip start/stop trim) */
    /* Passed-time fill clamped to the chevron window: track color before IN, accent only IN→position */
    .alb-seek-col .alb-seek { background: linear-gradient(to right, var(--slider-track-bg, rgba(255,255,255,0.15)) 0%, var(--slider-track-bg, rgba(255,255,255,0.15)) var(--in, 0%), var(--color-accent) var(--in, 0%), var(--color-accent) var(--val, 0%), var(--slider-track-bg, rgba(255,255,255,0.15)) var(--val, 0%)) !important; }
    .alb-seek-handle:hover, .alb-seek-handle:active { border-bottom-color: #ffd84b; }
    .alb-trim-dur { color: #ffd84b !important; }

    .alb-pl-transport .alb-pl-master-slider { flex: 1; min-width: 0; width: auto; }

/* ───── from NATURE-SOUNDS-MODULE/index.html ───── */
/* Title bar */
    #nat-section-playlist { margin-top: 1.5rem; }

    /* ── Presets: tabs + grid (mirrors Frequency Presets) ─────────────────── */
    #nat-main-parent-container .alb-tab.active { background: var(--color-accent); color: #111; border-color: var(--color-accent); }

    @media (max-width: 900px) { #nat-main-parent-container .alb-grid { grid-template-columns: repeat(2, 1fr); } }
    @media (max-width: 600px) { #nat-main-parent-container .alb-grid { grid-template-columns: 1fr; } }

    #nat-main-parent-container .alb-card {
        position: relative; display: flex; gap: 10px; min-height: 160px; cursor: pointer; overflow: hidden;
        background: var(--bg-dark-lighter); border: 1px solid var(--border-accent-light); border-radius: 4px;
        transition: border-color .2s ease, box-shadow .2s ease;
    }
    #nat-main-parent-container .alb-card:hover { border-color: var(--color-accent); box-shadow: 0 0 8px var(--glow-accent); }
    #nat-main-parent-container .alb-card-cover { width: 84px; flex-shrink: 0; object-fit: cover; background: #111; align-self: stretch; }
    #nat-main-parent-container .alb-card-body { flex: 1; min-width: 0; padding: 10px 10px 10px 0; display: flex; flex-direction: column; gap: 4px; }
    #nat-main-parent-container .alb-card-top { display: flex; align-items: flex-start; gap: 6px; }
    #nat-main-parent-container .alb-card-title { flex: 1; font-size: 14px; font-weight: 600; color: var(--text-primary, #fff); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
    /* .alb-info-btn look comes from .bws-info-badge */
    #nat-main-parent-container .alb-card-desc { font-size: 12px; color: var(--text-bright, rgba(255,255,255,0.8)); line-height: 1.4; display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; }
    #nat-main-parent-container .alb-card-empty { border: 1px dashed var(--border-accent-light); border-radius: 4px; min-height: 160px; opacity: 0.22; }

    .alb-badge-free { background: rgba(75,200,100,0.25); }
    .alb-badge-basic { background: rgba(75,150,255,0.25); }
    .alb-badge-premium { background: rgba(180,75,255,0.25); }
    .alb-badge-paid { background: rgba(255,216,75,0.18); }

    #nat-main-parent-container .alb-pager { display: flex; align-items: center; justify-content: center; gap: 14px; margin-top: 1rem; }
    #nat-main-parent-container .alb-pager button { padding: 6px 14px; border-radius: 4px; cursor: pointer; font-weight: 600; background: var(--bg-dark-lighter); border: 1px solid var(--border-accent-light); color: var(--color-accent); }
    #nat-main-parent-container .alb-pager button:disabled { opacity: 0.3; cursor: not-allowed; }
    #nat-main-parent-container .alb-pager-info { font-size: 13px; color: var(--text-muted); }
    #nat-main-parent-container .alb-personal-empty,
    #nat-main-parent-container .alb-grid-msg { grid-column: 1 / -1; text-align: center; color: var(--text-muted); padding: 2rem 1rem; font-size: 14px; line-height: 1.6; }

    /* ── Album info modal (cover/title/badge/description + track list) ─────── */

    /* ── Playlist segment (mirrors My Library Playlist) ───────────────────── */
    .alb-pl-card.alb-pl-dragging { opacity: 0.45; }
    .alb-pl-card.alb-pl-drop-above { box-shadow: inset 0 3px 0 0 var(--color-accent, #ffd84b); }
    .alb-pl-card.alb-pl-drop-below { box-shadow: inset 0 -3px 0 0 var(--color-accent, #ffd84b); }
    .alb-pl-drag-handle:hover { color: rgba(255,216,75,0.85); }
    .alb-pl-drag-handle:active { cursor: grabbing; }

    /* Row player control bar */
    .alb-play-btn:hover { background: rgba(255,216,75,0.14); }
    /* Seek + IN/OUT chevron range track (per-clip start/stop trim) */
    /* Passed-time fill clamped to the chevron window: track color before IN, accent only IN→position */
    .alb-seek-col .alb-seek { background: linear-gradient(to right, var(--slider-track-bg, rgba(255,255,255,0.15)) 0%, var(--slider-track-bg, rgba(255,255,255,0.15)) var(--in, 0%), var(--color-accent) var(--in, 0%), var(--color-accent) var(--val, 0%), var(--slider-track-bg, rgba(255,255,255,0.15)) var(--val, 0%)) !important; }
    .alb-seek-handle:hover, .alb-seek-handle:active { border-bottom-color: #ffd84b; }
    .alb-trim-dur { color: #ffd84b !important; }

    .alb-pl-transport .alb-pl-master-slider { flex: 1; min-width: 0; width: auto; }

/* ───── from SINGING-BOWLS-MODULE-V2/index.html ───── */
/* ── Bowl type custom dropdown trigger ── */
    .sbp-bowl-type-trigger {
        display: flex;
        align-items: center;
        justify-content: space-between;
        text-align: left;
        cursor: pointer;
        width: 100%;
    }

    /* .sbp-btd-info is all that is left of the old bowl-type dropdown: V2 uses
       the shared .hpp-preset-dropdown widget, and only kept this class on the
       ⓘ beside the trigger. */
    .sbp-btd-info { margin-right: 0.6rem; }
    .sbp-bowl-type-info { margin-right: 0; }

    /* ── Play pattern info modal (animated bowl-order graphic) ── */
    .sbp-ppm-overlay,
    .sbp-bowl-info-overlay {
        position: fixed;
        inset: 0;
        z-index: 9000;
        display: flex;
        align-items: center;
        justify-content: center;
        gap: 16px;
        padding: 24px;
        overflow: hidden;
        font-family: 'Roboto', -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
    }
    .sbp-ppm-modal {
        width: 100%;
        max-width: 560px;
        max-height: calc(100vh - 100px);
        display: flex;
        flex-direction: column;
        border: 1px solid var(--color-accent, #ffd84b);
        border-radius: 8px;
        box-shadow: 0 0 12px rgba(255,216,75,0.4), 0 8px 32px rgba(0,0,0,0.7);
        overflow: hidden;
    }
    .sbp-ppm-head,
    .sbp-bim-head {
        display: flex;
        align-items: center;
        gap: 0.6rem;
        padding: 1rem 1rem 0.75rem;
        flex-shrink: 0;
    }
    .sbp-ppm-badge {
        width: 24px;
        height: 24px;
        border-radius: 50%;
        background: var(--color-accent, #ffd84b);
        color: #000;
        flex-shrink: 0;
        display: flex;
        align-items: center;
        justify-content: center;
        font-family: 'Courier New', Courier, monospace;
        font-size: 15px;
        font-weight: 700;
    }
    .sbp-ppm-title,
    .sbp-bim-title {
        flex: 1;
        color: var(--text-primary, #fff);
        font-size: 16px;
        font-weight: 700;
    }
    .sbp-ppm-close:hover { background: #f44336; color: #fff; }
    .sbp-ppm-underline,
    .sbp-bim-underline {
        height: 1px;
        background: rgba(128,128,128,0.4);
        margin: 0 1rem;
        flex-shrink: 0;
    }
    .sbp-ppm-body {
        overflow-y: auto;
        padding: 1.25rem 1rem;
        scrollbar-width: thin;
        scrollbar-color: var(--color-accent, #ffd84b) #000;
    }
    .sbp-ppm-body::-webkit-scrollbar { width: 8px; }
    .sbp-ppm-body::-webkit-scrollbar-track { background: #000; }
    .sbp-ppm-body::-webkit-scrollbar-thumb { background: var(--color-accent, #ffd84b); border-radius: 4px; }
    .sbp-ppm-graphic {
        position: relative;
        display: flex;
        align-items: center;
        justify-content: center;
        flex-wrap: wrap;
        gap: 10px;
        padding: 28px 14px;
        margin-bottom: 18px;
        background: rgba(0,0,0,0.35);
        border: 1px solid rgba(255,216,75,0.12);
        border-radius: 8px;
    }
    .sbp-ppm-graphic::before {
        content: '';
        position: absolute;
        left: 24px;
        right: 24px;
        top: 50%;
        height: 2px;
        background: rgba(255,216,75,0.12);
        transform: translateY(-50%);
    }
    .sbp-ppm-bowl {
        position: relative;
        z-index: 1;
        width: 40px;
        height: 40px;
        border-radius: 50%;
        display: flex;
        align-items: center;
        justify-content: center;
        background: rgba(40,40,40,0.95);
        border: 1.5px solid rgba(255,216,75,0.35);
        color: rgba(255,255,255,0.55);
        font-size: 14px;
        font-weight: 600;
        transition: transform 0.18s ease, background 0.18s ease, box-shadow 0.18s ease, color 0.18s ease, border-color 0.18s ease;
    }
    .sbp-ppm-bowl.active {
        background: var(--color-accent, #ffd84b);
        color: #000;
        border-color: var(--color-accent, #ffd84b);
        transform: scale(1.22);
        box-shadow: 0 0 16px rgba(255,216,75,0.75);
    }
    .sbp-ppm-desc {
        color: rgba(248,249,250,0.92);
        font-size: 14px;
        line-height: 1.6;
        margin-bottom: 18px;
    }
    .sbp-ppm-select-btn { width: 100%; }

    /* ── Bowl type info modal ── */
    /* Carousel nav — yellow circle buttons with black chevrons, beside the modal */
    .sbp-bowl-info-modal {
        width: 100%;
        max-width: 800px;
        min-width: 0;
        max-height: calc(100vh - 100px);
        display: flex;
        flex-direction: column;
        border: 1px solid var(--color-accent, #ffd84b);
        border-radius: 8px;
        box-shadow: 0 0 12px rgba(255,216,75,0.4), 0 8px 32px rgba(0,0,0,0.7);
        overflow: hidden;
    }
    /* .sbp-bim-badge look comes from .bws-info-badge */
    .sbp-bim-close:hover { background: #f44336; color: #fff; }
    .sbp-bim-body {
        overflow-y: auto;
        padding: 1rem;
        scrollbar-width: thin;
        scrollbar-color: var(--color-accent, #ffd84b) #000;
    }
    .sbp-bim-body::-webkit-scrollbar { width: 8px; }
    .sbp-bim-body::-webkit-scrollbar-track { background: #000; }
    .sbp-bim-body::-webkit-scrollbar-thumb {
        background: var(--color-accent, #ffd84b);
        border-radius: 4px;
    }
    .sbp-bim-image {
        width: 100%;
        max-height: 380px;
        object-fit: cover;
        border-radius: 6px;
        margin-bottom: 14px;
        display: block;
    }
    .sbp-bim-subtitle {
        color: rgba(248,249,250,0.9);
        font-size: 13px;
        font-weight: 500;
        font-style: italic;
        margin-bottom: 10px;
    }
    .sbp-bim-details {
        margin-bottom: 12px;
        padding: 0.5rem 0.625rem;
        border-radius: 4px;
        background: rgba(255,216,75,0.05);
        border: 1px solid rgba(255,216,75,0.12);
    }
    /* Best For (2/3) + Play Preview button (1/3) in one row */
    .sbp-bim-details-row {
        display: flex;
        gap: 0.75rem;
        margin-bottom: 12px;
        align-items: stretch;
    }
    .sbp-bim-details-row .sbp-bim-details {
        flex: 2;
        margin-bottom: 0;
        display: flex;
        align-items: center;
    }
    .sbp-bim-preview-btn {
        flex: 1;
        min-width: 0;
        white-space: nowrap;
    }
    /* Full-width yellow "Set … Preset in Player" button at the bottom */
    @media (max-width: 560px) {
        .sbp-bim-details-row { flex-direction: column; }
        .sbp-bowl-info-overlay { padding: 10px; gap: 6px; }
    }
    .sbp-bim-detail-label {
        color: var(--color-accent, #ffd84b);
        font-weight: 600;
    }
    /* Sound Characteristics + Benefits side by side */
    .sbp-bim-sections-row {
        display: flex;
        gap: 0.75rem;
        margin-top: 10px;
        align-items: stretch;
    }
    .sbp-bim-sections-row .sbp-bim-section {
        flex: 1;
        min-width: 0;
        margin-top: 0;
    }
    @media (max-width: 560px) {
        .sbp-bim-sections-row { flex-direction: column; gap: 0; }
        .sbp-bim-sections-row .sbp-bim-section { margin-top: 10px; }
    }
    .sbp-bim-section-title {
        color: var(--color-accent, #ffd84b);
        font-size: 13px;
        font-weight: 600;
        margin-bottom: 8px;
        padding-bottom: 4px;
        border-bottom: 1px solid rgba(255,216,75,0.2);
    }
    .sbp-bim-section-item {
        color: rgba(248,249,250,0.8);
        font-size: 12px;
        line-height: 19px;
        margin-bottom: 3px;
    }
    /* Frequency-preset scale display (chips of note + Hz) */
    .sbp-bim-scale-label { color: rgba(248,249,250,0.6); font-size: 12px; margin-bottom: 10px; letter-spacing: 0.3px; }
    .sbp-bim-scale { display: flex; flex-wrap: wrap; gap: 8px; margin-bottom: 14px; }
    .sbp-bim-note {
        flex: 1 1 auto; min-width: 64px; display: flex; flex-direction: column; align-items: center; gap: 2px;
        padding: 0.55rem 0.4rem; border-radius: 6px; background: rgba(255,255,255,0.04); border: 1px solid rgba(255,216,75,0.12);
    }
    .sbp-bim-note-name { color: var(--color-accent, #ffd84b); font-size: 17px; font-weight: 700; line-height: 1; }
    .sbp-bim-note-hz { color: rgba(248,249,250,0.7); font-size: 11px; }
    .bt-info-modal .bt-im-fn-tag,
    .ft-info-modal .ft-im-fn-tag,
    .it-info-modal .it-im-fn-tag,
    .sbp-bim-fn-tag {
        display: inline-block; color: #000; background: var(--color-accent, #ffd84b); font-size: 11px; font-weight: 700;
        text-transform: uppercase; letter-spacing: 0.4px; padding: 2px 8px; border-radius: 10px; margin-bottom: 12px;
    }

    /* ── Session builder category bands ── */
    .sbp-builder-bands { display: flex; flex-direction: column; gap: 1.75rem; width: 100%; }
    .sbp-builder-band {
        border: 1px solid rgba(255,216,75,0.14);
        border-radius: 8px;
        padding: 1rem 1.1rem 1.15rem;
        background: rgba(255,255,255,0.015);
    }
    .sbp-band-heading {
        font-size: 13px;
        font-weight: 700;
        letter-spacing: 0.6px;
        text-transform: uppercase;
        color: var(--color-accent, #ffd84b);
        padding-bottom: 9px;
        margin-bottom: 1rem;
        border-bottom: 1px solid rgba(255,216,75,0.22);
    }
    .sbp-band-grid {
        display: grid;
        grid-template-columns: 1fr 1fr;
        gap: 1.25rem 2rem;
        align-items: start;
    }
    .sbp-band-col { display: flex; flex-direction: column; gap: 1.5rem; min-width: 0; }
    @media screen and (max-width: 768px) {
        .sbp-band-grid { grid-template-columns: 1fr; gap: 1.25rem; }
    }

/* ───── from SPECIAL-MODULE/index.html ───── */
/* Title bar */
    #spc-section-playlist { margin-top: 1.5rem; }

    /* ── Presets: tabs + grid (mirrors Frequency Presets) ─────────────────── */
    #spc-main-parent-container .alb-tab.active { background: var(--color-accent); color: #111; border-color: var(--color-accent); }

    @media (max-width: 900px) { #spc-main-parent-container .alb-grid { grid-template-columns: repeat(2, 1fr); } }
    @media (max-width: 600px) { #spc-main-parent-container .alb-grid { grid-template-columns: 1fr; } }

    #spc-main-parent-container .alb-card {
        position: relative; display: flex; gap: 10px; min-height: 160px; cursor: pointer; overflow: hidden;
        background: var(--bg-dark-lighter); border: 1px solid var(--border-accent-light); border-radius: 4px;
        transition: border-color .2s ease, box-shadow .2s ease;
    }
    #spc-main-parent-container .alb-card:hover { border-color: var(--color-accent); box-shadow: 0 0 8px var(--glow-accent); }
    #spc-main-parent-container .alb-card-cover { width: 84px; flex-shrink: 0; object-fit: cover; background: #111; align-self: stretch; }
    #spc-main-parent-container .alb-card-body { flex: 1; min-width: 0; padding: 10px 10px 10px 0; display: flex; flex-direction: column; gap: 4px; }
    #spc-main-parent-container .alb-card-top { display: flex; align-items: flex-start; gap: 6px; }
    #spc-main-parent-container .alb-card-title { flex: 1; font-size: 14px; font-weight: 600; color: var(--text-primary, #fff); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
    /* .alb-info-btn look comes from .bws-info-badge */
    #spc-main-parent-container .alb-card-desc { font-size: 12px; color: var(--text-bright, rgba(255,255,255,0.8)); line-height: 1.4; display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; }
    #spc-main-parent-container .alb-card-empty { border: 1px dashed var(--border-accent-light); border-radius: 4px; min-height: 160px; opacity: 0.22; }

    .alb-badge-free { background: rgba(75,200,100,0.25); }
    .alb-badge-basic { background: rgba(75,150,255,0.25); }
    .alb-badge-premium { background: rgba(180,75,255,0.25); }
    .alb-badge-paid { background: rgba(255,216,75,0.18); }

    #spc-main-parent-container .alb-pager { display: flex; align-items: center; justify-content: center; gap: 14px; margin-top: 1rem; }
    #spc-main-parent-container .alb-pager button { padding: 6px 14px; border-radius: 4px; cursor: pointer; font-weight: 600; background: var(--bg-dark-lighter); border: 1px solid var(--border-accent-light); color: var(--color-accent); }
    #spc-main-parent-container .alb-pager button:disabled { opacity: 0.3; cursor: not-allowed; }
    #spc-main-parent-container .alb-pager-info { font-size: 13px; color: var(--text-muted); }
    #spc-main-parent-container .alb-personal-empty,
    #spc-main-parent-container .alb-grid-msg { grid-column: 1 / -1; text-align: center; color: var(--text-muted); padding: 2rem 1rem; font-size: 14px; line-height: 1.6; }

    /* ── Album info modal (cover/title/badge/description + track list) ─────── */

    /* ── Playlist segment (mirrors My Library Playlist) ───────────────────── */
    .alb-pl-card.alb-pl-dragging { opacity: 0.45; }
    .alb-pl-card.alb-pl-drop-above { box-shadow: inset 0 3px 0 0 var(--color-accent, #ffd84b); }
    .alb-pl-card.alb-pl-drop-below { box-shadow: inset 0 -3px 0 0 var(--color-accent, #ffd84b); }
    .alb-pl-drag-handle:hover { color: rgba(255,216,75,0.85); }
    .alb-pl-drag-handle:active { cursor: grabbing; }

    /* Row player control bar */
    .alb-play-btn:hover { background: rgba(255,216,75,0.14); }
    /* Seek + IN/OUT chevron range track (per-clip start/stop trim) */
    /* Passed-time fill clamped to the chevron window: track color before IN, accent only IN→position */
    .alb-seek-col .alb-seek { background: linear-gradient(to right, var(--slider-track-bg, rgba(255,255,255,0.15)) 0%, var(--slider-track-bg, rgba(255,255,255,0.15)) var(--in, 0%), var(--color-accent) var(--in, 0%), var(--color-accent) var(--val, 0%), var(--slider-track-bg, rgba(255,255,255,0.15)) var(--val, 0%)) !important; }
    .alb-seek-handle:hover, .alb-seek-handle:active { border-bottom-color: #ffd84b; }
    .alb-trim-dur { color: #ffd84b !important; }

    .alb-pl-transport .alb-pl-master-slider { flex: 1; min-width: 0; width: auto; }

/* ═══════════════════════════════════════════════════════════════════════════
   TINNITUS RELIEF MODULE (tin-)
   Step-by-step wizard: prepare → pitch match → loudness → sound → session
   ═══════════════════════════════════════════════════════════════════════════ */

/* Warning triangle on the left, text beside it. */
.tin-disclaimer {
    display: flex;
    align-items: center;
    gap: 0.85rem;
    background: rgba(255, 152, 0, 0.08);
    border: 1px solid rgba(255, 152, 0, 0.35);
    border-radius: 4px;
    padding: 0.75rem 1rem;
    font-size: 13px;
    line-height: 1.55;
    color: var(--text-bright, rgba(255,255,255,0.8));
}
.tin-disclaimer-icon {
    flex: 0 0 auto;
    width: 32px;
    height: 32px;
    color: var(--color-accent, #ffd84b);
}

.tin-welcome-back {
    display: flex;
    align-items: center;
    gap: 1rem;
    flex-wrap: wrap;
    background: var(--bg-accent-light, rgba(255,216,75,0.1));
    border: 1px solid var(--border-accent-light, rgba(255,216,75,0.2));
    border-radius: 4px;
    padding: 0.6rem 1rem;
    font-size: 13px;
    color: var(--color-accent, #ffd84b);
}
.tin-welcome-back .bws-button-a { min-width: 0; padding: 0.35rem 0.9rem; font-size: 12px; }

/* ── Step blocks ─────────────────────────────────────────────────────────── */
.tin-step {
    background: var(--bg-dark-light, rgba(0,0,0,0.2));
    border: 1px solid var(--border-accent-medium, rgba(255,216,75,0.15));
    border-radius: 4px;
    padding: 1rem;
    transition: opacity 0.3s ease, border-color 0.3s ease;
}
.tin-step.tin-locked { opacity: 0.45; }
/* Accordion: only the step carrying .tin-open shows its body, so a locked step
   and a merely collapsed one hide the same way. */
.tin-step:not(.tin-open) .tin-step-body:not(.tin-animating) { display: none; }

/* The whole header is the accordion toggle, so it is a button stripped back to
   look like the row it replaced. */
.tin-step-header {
    display: flex;
    align-items: center;
    gap: 0.75rem;
    width: 100%;
    padding: 0;
    background: none;
    border: none;
    text-align: left;
    font: inherit;
    color: inherit;
    cursor: pointer;
}
.tin-step.tin-locked .tin-step-header { cursor: not-allowed; }
.tin-step-caret {
    flex: 0 0 auto;
    display: flex;
    align-items: center;
    color: var(--color-accent, #ffd84b);
}
.tin-step-caret .caret-up { display: none; }
.tin-step.tin-open .tin-step-caret .caret-up { display: block; }
.tin-step.tin-open .tin-step-caret .caret-down { display: none; }
.tin-step.tin-locked .tin-step-caret { color: var(--text-muted, rgba(255,255,255,0.5)); }

/* Separates the relief-sound choice from the session controls below it. */
.tin-step-divider {
    width: 100%;
    height: 0;
    margin: 0.4rem 0 0;
    border: 0;
    border-top: 1px solid var(--border-accent-medium, rgba(255,216,75,0.15));
}
.tin-step-num {
    flex: 0 0 auto;
    display: flex;
    align-items: center;
    justify-content: center;
    width: 26px;
    height: 26px;
    border-radius: 50%;
    background: var(--color-accent, #ffd84b);
    border: none;
    color: #000;
    font-size: 14px;
    font-weight: 700;
}
/* The step number stays a yellow/black badge in every state — completion is
   signalled by .tin-step-status, not by recolouring the numeral. */
.tin-step-title {
    font-size: 15px;
    font-weight: 600;
    color: var(--text-bright, rgba(255,255,255,0.85));
}
.tin-step-status {
    margin-left: auto;
    font-size: 14px;
    color: #28a745;
}
.tin-step.tin-locked .tin-step-status { color: var(--text-muted, rgba(255,255,255,0.5)); }

/* The top gap is padding, not margin, so it is inside the measured height and
   collapses together with the body while the accordion animates. */
.tin-step-body {
    display: flex;
    flex-direction: column;
    gap: 0.9rem;
    padding-top: 0.9rem;
}
/* While opening or closing the body keeps its own display (flex here, grid in
   step 1) and is driven by an explicit height set from JS. */
.tin-step-body.tin-animating {
    overflow: hidden;
    transition: height 300ms ease, opacity 300ms ease;
}
@media (prefers-reduced-motion: reduce) {
    .tin-step-body.tin-animating { transition: none; }
}
.tin-guide {
    margin: 0;
    font-size: 13px;
    line-height: 1.6;
    color: var(--text-muted, rgba(255,255,255,0.6));
}
.tin-guide strong { color: var(--text-bright, rgba(255,255,255,0.85)); }

.tin-row {
    display: flex;
    align-items: center;
    gap: 0.75rem;
    flex-wrap: wrap;
}
/* Label above its buttons, both centred in the column. */
.tin-row-stack {
    flex-direction: column;
    align-items: center;
    text-align: center;
    gap: 0.5rem;
}
.tin-row-stack > * { justify-content: center; }

.tin-step-actions { display: flex; gap: 0.75rem; flex-wrap: wrap; }
.tin-step-actions-center { justify-content: center; margin-top: 1.25rem; }
/* Headphones glyph in the step-1 confirm button (mirrors fa-headphones,
   the binaural module's icon), inlined since this page has no icon font. */
.tin-step-actions .bws-button-a { display: inline-flex; align-items: center; gap: 0.5rem; }
.tin-btn-icon { flex: none; width: 22px; height: 22px; }
/* Icon then label, spaced like the session transport's own play button. While a
   button is sounding it takes .bws-button-a.playing — the transport's solid red
   — so a playing control looks the same wherever it is in the module. */
.tin-tone-btn { min-width: 150px; gap: 7px; }
.tin-play-icon { flex: none; }
.tin-volume-inline {
    flex: 1;
    min-width: 200px;
    display: flex;
    align-items: center;
    gap: 0.5rem;
}

/* ── Step 1 — three columns: what to know, the answers, the picture ────── */
.tin-step1-cols {
    display: grid;
    grid-template-columns: minmax(0, 1fr) minmax(0, 1fr) minmax(286px, 390px);
    gap: 1.25rem;
    align-items: start;
}
.tin-step1-intro { display: flex; flex-direction: column; gap: 0.75rem; min-width: 0; }
/* Small accent headings labelling the two blocks in the first column. The
   gap above pulls each one closer to the block it names than to the one
   before it. */
.tin-intro-head {
    margin: 0;
    font-size: 13px;
    font-weight: 700;
    letter-spacing: 0.06em;
    text-transform: uppercase;
    color: var(--color-accent, #ffd84b);
}
.tin-step1-intro .tin-intro-head + * { margin-top: -0.25rem; }
.tin-step1-intro .tin-intro-head:not(:first-child) { margin-top: 0.35rem; }
/* In a narrow column the notice is several lines tall, so the triangle sits at
   the top of the text rather than floating in the middle of it. */
.tin-step1-intro .tin-disclaimer { align-items: flex-start; }
.tin-step1-controls { display: flex; flex-direction: column; gap: 0.75rem; min-width: 0; }
/* Heads the answer column. Centred like the choices under it, with extra
   space below so it reads as a title for the whole column rather than a
   label for the first question. */
.tin-controls-head {
    text-align: center;
    margin-bottom: 0.5rem;
}

.tin-head-figure {
    margin: 0;
    padding: 0.5rem;
    border: 1px solid var(--border-accent-medium, rgba(255,216,75,0.15));
    border-radius: 6px;
    background: #000;
    display: flex;
    flex-direction: column;
    gap: 0.4rem;
}
/* Two layers in the same box so a change crossfades. The stack keeps the
   square footprint, so nothing reflows while the images trade places. */
.tin-head-stack {
    position: relative;
    aspect-ratio: 1 / 1;
    /* The renders are square with a black backdrop, so the frame stays dark in
       both themes rather than boxing a black image in a pale panel. */
    background: #000;
    border-radius: 4px;
    overflow: hidden;
}
.tin-head-img {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    display: block;
    opacity: 0;
    transition: opacity 500ms ease;
}
.tin-head-img.is-on { opacity: 1; }
@media (prefers-reduced-motion: reduce) {
    .tin-head-img { transition: none; }
}
/* Sits on the figure's black panel, so it keeps its light colour in both
   themes instead of following --text-muted. */
.tin-head-caption {
    text-align: center;
    font-size: 12px;
    color: rgba(255, 255, 255, 0.6);
    min-height: 1.2em;
}

/* Too narrow for three: the notice takes the full width above the answers and
   the picture, which keep their own two columns. */
@media (max-width: 1100px) {
    .tin-step1-cols { grid-template-columns: minmax(0, 1fr) minmax(286px, 390px); }
    .tin-step1-intro { grid-column: 1 / -1; }
}
@media (max-width: 700px) {
    .tin-step1-cols { grid-template-columns: minmax(0, 1fr); }
    .tin-head-figure { max-width: 390px; align-self: center; }
}

/* ── Ear selection ───────────────────────────────────────────────────────── */
.tin-ear-group,
.tin-char-group { display: flex; gap: 0.5rem; flex-wrap: wrap; }
/* Sound-type choices read as a stacked list, one per line. The group is only
   as wide as its longest label, and stretching makes all three match it. */
.tin-char-group {
    flex-direction: column;
    align-items: stretch;
    width: fit-content;
    flex-wrap: nowrap;
}
.tin-char-group .tin-char-btn { text-align: center; }
/* Breathing room between the ear buttons above and this group's label. */
.tin-char-row { margin-top: 1.75rem; }
/* Two lines of choices: "Inside the head" alone, then the three ears. */
.tin-ear-group { flex-direction: column; align-items: center; }
.tin-ear-choice-line { display: flex; gap: 0.5rem; flex-wrap: wrap; justify-content: center; }
.tin-ear-btn,
.tin-char-btn {
    padding: 0.45rem 1rem;
    border-radius: 4px;
    background: var(--input-bg, rgba(17,17,17,0.8));
    border: 1px solid var(--border-accent-light, rgba(255,216,75,0.2));
    color: var(--text-muted, rgba(255,255,255,0.6));
    font-size: 13px;
    cursor: pointer;
    transition: all 0.15s ease;
}
.tin-ear-btn:hover,
.tin-char-btn:hover { border-color: var(--color-accent, #ffd84b); color: var(--text-bright, rgba(255,255,255,0.85)); }
.tin-ear-btn.active,
.tin-char-btn.active {
    background: var(--bg-accent-medium, rgba(255,216,75,0.2));
    border-color: var(--color-accent, #ffd84b);
    color: var(--color-accent, #ffd84b);
    font-weight: 600;
}
/* Advice line that follows the selected tinnitus character. */
.tin-char-note {
    font-size: 12.5px;
    line-height: 1.5;
    color: var(--text-muted, rgba(255,255,255,0.6));
    padding: 0.5rem 0.75rem;
    border-left: 2px solid var(--border-accent-medium, rgba(255,216,75,0.24));
    background: var(--bg-dark-light, rgba(0,0,0,0.15));
    border-radius: 0 4px 4px 0;
}
.tin-char-note:empty { display: none; }

/* ── Pitch matching ──────────────────────────────────────────────────────── */
.tin-freq-row { justify-content: space-between; }
.tin-freq-readout {
    font-size: 22px;
    font-weight: 700;
    color: var(--color-accent, #ffd84b);
    font-variant-numeric: tabular-nums;
}
/* −oct −100 −10 −1 [Hz] +1 +10 +100 +oct — wraps rather than overflowing */
.tin-freq-adjust { gap: 0.4rem; flex-wrap: wrap; justify-content: flex-start; }

/* Small secondary buttons (adjust / A-B check / restart) — deliberately NOT
   .bws-icon-button (that class is a 60px-tall flex tile for icon grids).
   The card preview button is NOT one of these: it starts and stops sound, so it
   is a .bws-button-a like every other play control, and listing it here would
   grey it back out — this group is later in the file and would win. */
.tin-adjust-btn, .tin-ab-btn, .tin-restart-btn {
    background: var(--bg-dark-medium, rgba(0,0,0,0.4));
    border: 1px solid var(--border-accent-light, rgba(255,216,75,0.2));
    border-radius: 4px;
    color: var(--text-muted, rgba(255,255,255,0.6));
    cursor: pointer;
    transition: all 0.15s ease;
    white-space: nowrap;
}
.tin-adjust-btn:hover, .tin-ab-btn:hover, .tin-restart-btn:hover {
    background: var(--bg-accent-light, rgba(255,216,75,0.1));
    border-color: var(--color-accent, #ffd84b);
    color: var(--text-bright, rgba(255,255,255,0.8));
}
.tin-adjust-btn {
    min-width: 40px;
    height: 34px;
    padding: 0 0.5rem;
    font-size: 12.5px;
    font-weight: 600;
    font-variant-numeric: tabular-nums;
}
.tin-freq-adjust .bbp-frequency-input { width: 90px; text-align: center; }

/* ── Step 2 matching panel ───────────────────────────────────────────────── */
.tin-tone-list { display: flex; flex-direction: column; gap: 0.75rem; }
.tin-tone-card {
    display: flex;
    flex-direction: column;
    gap: 0.6rem;
    padding: 0.75rem;
    border: 1px solid var(--border-accent-light, rgba(255,216,75,0.2));
    border-radius: 6px;
    background: var(--bg-dark-lighter, rgba(0,0,0,0.1));
}
.tin-tone-card-head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 0.5rem;
}
.tin-tone-label {
    font-size: 13px;
    font-weight: 700;
    color: var(--color-accent, #ffd84b);
}
/* The standard red round X, but sitting in the card header row rather than
   floating in a corner. */
.tin-tone-remove { position: static; flex-shrink: 0; }
/* One ear's controls. When both ears are matched separately the two sides sit
   side by side, each in its own panel so they never blur together. */
.tin-ear-line { display: flex; flex-direction: column; gap: 0.5rem; }
/* One side's panels — frequency, then volume under it. The column is the grid
   item, so both sides still sit in two columns however many panels each holds. */
.tin-ear-col { display: flex; flex-direction: column; gap: 0.75rem; min-width: 0; }
.tin-ear-cols {
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: 0.75rem 1rem;
    align-items: start;
}
/* Only one place to match — the single panel keeps a column's width and sits
   in the middle rather than stretching across the card. */
.tin-ear-cols-single {
    grid-template-columns: minmax(0, 50%);
    justify-content: center;
}
/* Each side gets its own panel, framed like the tone card that holds them,
   rather than a left rail. */
.tin-ear-line-tagged {
    padding: 0.75rem;
    border: 1px solid var(--border-accent-light, rgba(255,216,75,0.2));
    border-radius: 6px;
    background: var(--bg-dark-lighter, rgba(0,0,0,0.1));
}

/* Volume sits on its own row in the split layout so both columns line up.
   Inside a column the controls drop their desktop minimums so nothing
   overflows the half-width track. */
.tin-vol-row { justify-content: flex-start; }
.tin-vol-row .tin-volume-inline { flex: 1; min-width: 0; }
/* Fixed width so the two sides match each other and nothing jumps when the
   label switches between "Play left ear" and "Stop". */
.tin-ear-line-tagged .tin-tone-btn { flex: 0 0 auto; width: 160px; min-width: 0; max-width: 100%; padding-left: 0.5rem; padding-right: 0.5rem; }
.tin-ear-line-tagged .tin-freq-readout { font-size: 19px; }
.tin-ear-line-tagged .tin-volume-inline { min-width: 0; }
@media (max-width: 820px) {
    .tin-ear-cols { grid-template-columns: minmax(0, 1fr); }
}
.tin-tone-both { justify-content: flex-start; gap: 0.75rem; flex-wrap: wrap; }
/* Sits below the tone cards, so it needs its own breathing room. */
.tin-tone-add-row { gap: 0.75rem; margin-top: 1rem; }
.tin-tone-add:disabled { opacity: 0.4; cursor: default; }
.tin-hint { font-size: 12px; color: var(--text-muted, rgba(255,255,255,0.5)); }

/* Step 2's pitch / band / placement sliders sit in COLUMN flex containers,
   where .bws-effect-slider's `flex: 1` would grow them vertically and swallow
   the 6px height. Pin them flat so they read like the volume sliders. */
.tin-ear-line > .bws-effect-slider,
.tin-pan-panel > .bws-effect-slider {
    flex: 0 0 auto;
    width: 100%;
    height: 6px;
}

/* Tone matching and placement share one row. Placement is hidden unless the
   tinnitus is heard inside the head, and a hidden flex item takes no space, so
   the tone panel simply fills the row on its own. */
.tin-match-row {
    display: flex;
    flex-wrap: wrap;
    align-items: flex-start;
    gap: 1rem;
}
.tin-match-row > .tin-tone-panel { flex: 1 1 360px; min-width: 0; }
.tin-match-row > .tin-pan-panel  { flex: 1 1 280px; min-width: 0; }
/* Framed like the tone's own panel, so the two read as a pair. */
.tin-match-row > .tin-pan-panel {
    padding: 0.75rem;
    border: 1px solid var(--border-accent-light, rgba(255,216,75,0.2));
    border-radius: 6px;
    background: var(--bg-dark-lighter, rgba(0,0,0,0.1));
}
/* Titles the panels of step 2. The panels are flex columns with their own gap,
   so the title only needs to lose the heading's default margins. */
.tin-line-head { margin: 0; font-size: 12px; }

/* Moved under the tone panel, placement is a second item in that panel's own
   column: same width, framed the same way, directly below it. */
.tin-ear-cols-single > .tin-pan-panel {
    padding: 0.75rem;
    border: 1px solid var(--border-accent-light, rgba(255,216,75,0.2));
    border-radius: 6px;
    background: var(--bg-dark-lighter, rgba(0,0,0,0.1));
}

/* Placement + noise-band panels */
.tin-pan-panel,
#tin-hiss-panel { display: flex; flex-direction: column; gap: 0.5rem; }
.tin-pan-scale {
    display: flex;
    justify-content: space-between;
    font-size: 11.5px;
    color: var(--text-muted, rgba(255,255,255,0.5));
}
.tin-band-row { display: flex; flex-direction: column; gap: 0.35rem; }

/* The band's two edges share one track (the dual-handle slider from the
   singing-bowls module); the selected span is painted inline by the module. */
.tin-band-track { margin: 12px 0 4px; }
.tin-band-scale {
    display: flex;
    justify-content: space-between;
    font-size: 11.5px;
    color: var(--text-muted, rgba(255,255,255,0.5));
}

.tin-ab-btn { padding: 0.45rem 0.9rem; font-size: 12px; }

/* ── Sound cards ─────────────────────────────────────────────────────────── */
/* Four to a row at most, so the cards past the fourth start a second row instead
   of stretching the first one on a wide screen — and they keep exactly the width
   the first row has. The track floor is the larger of 210px and a quarter of the
   row, which is what caps the count; below ~900px the 210px wins again and the
   grid packs as many as fit, as before. */
.tin-sound-grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(max(210px, calc((100% - 3 * 0.75rem) / 4)), 1fr));
    gap: 0.75rem;
}
/* ── Session card (sticky) ─────────────────────────────────────
   Two padded strips in one card — how long the session runs above, the transport
   below — which is the singing bowls session card (.sbp-session-card) exactly:
   same chrome, same split, same label-left duration field. The duration lives here
   rather than in the wizard because it is a setting you reach for while listening,
   and the wizard step scrolls away.

   Deliberately NOT scoped under #tin-main-parent-container: the shared bottom-bar
   rules address this as `.bws-sticky-wrap > …`, and an id in front of the class
   here would out-specify them — the card would keep this resting chrome instead of
   the pinned plate, and never stick at all. .sbp-session-card and .mex-console-box
   are written the same way, for the same reason. */
.tin-session-card {
    background: var(--bg-dark-light);
    border: 1px solid var(--border-accent-light);
    border-radius: 4px;
}
.tin-controls-top {
    display: flex; align-items: center; justify-content: space-between;
    gap: 1.5rem; padding: .75rem;
    border-bottom: 1px solid var(--border-accent-light);
}
.tin-controls-bottom {
    display: flex; align-items: center; gap: 1rem; padding: .6rem .75rem;
}
.tin-controls-bottom .hpp-player-row { flex: 1; min-width: 0; margin-top: 0; }
/* Same field-label recipe as the bowls card's .sbp-ctrl-label. */
.tin-ctrl-label {
    font-size: 11px; color: var(--text-muted, #aaa); font-weight: 700;
    text-transform: uppercase; letter-spacing: .08em; white-space: nowrap; flex: 0 0 auto;
}
.tin-duration-group { display: flex; align-items: center; gap: .6rem; flex-wrap: wrap; }
/* Locked while the app-wide notch is the therapy: it runs until it is switched
   off, so a session length has nothing to apply to. The label dims with its
   controls, so the strip reads as one thing being unavailable rather than a few
   dead boxes next to a live title. */
.tin-duration-group.is-locked { opacity: 0.45; }
.tin-duration-group.is-locked .tin-dur-btn { cursor: not-allowed; }
/* Nothing to play: either no relief sound is picked in step 3, or "Notch Every
   Module" is the therapy and this module makes no sound of its own. Both strips
   of the sticky card grey out together, so it reads as one transport being
   unavailable rather than a scatter of dead buttons. The controls themselves
   carry `disabled` (updateSessionUI), which is what actually blocks input --
   this is the look. The duration group drops its own dimming here so the two
   do not multiply into an unreadable strip. */
.tin-session-card.tin-session-inert .tin-controls-top,
.tin-session-card.tin-session-inert .tin-controls-bottom { opacity: 0.45; }
.tin-session-card.tin-session-inert .tin-duration-group.is-locked { opacity: 1; }
.tin-session-card.tin-session-inert .hpp-player-btn,
.tin-session-card.tin-session-inert .tin-dur-btn,
.tin-session-card.tin-session-inert .tin-dur-input,
.tin-session-card.tin-session-inert input[type="range"] { cursor: not-allowed; }
.tin-dur-input:disabled,
.tin-dur-btn:disabled { cursor: not-allowed; }
.tin-dur-btn:disabled:hover {
    border-color: var(--border-accent-light, rgba(255,216,75,0.2));
    color: var(--text-muted, rgba(255,255,255,0.6));
}
.tin-dur-btn.active:disabled:hover {
    border-color: var(--color-accent, #ffd84b);
    color: var(--color-accent, #ffd84b);
}
.tin-dur-pills { display: flex; align-items: center; gap: .5rem; flex-wrap: wrap; }

/* Typed session length. The boxes themselves are the shared .ft-pause-hms family
   (joined by class name above, light theme included) — this is only the grouping,
   and the gap that separates it from the quick-pick chips beside it. */
.tin-duration {
    display: flex;
    align-items: center;
    gap: 0.25rem;
    margin-left: 0.4rem;
}

/* Info badge, upper-right of every card in step 3 — the sound cards and the notch
   card alike. Absolute, so it does not take a line from the card's own content;
   the title gets right padding to clear it. */
.tin-sound-card,
.tin-notch-card { position: relative; }
.tin-info-btn {
    position: absolute;
    top: 8px;
    right: 8px;
    z-index: 1;
}
.tin-sound-card .tin-sound-name,
.tin-notch-card .tin-sound-name { padding-right: 30px; }

/* ── Card info modal (tin-im-) ───────────────────────────────────
   Same shell as the timer modules' preset info modals — overlay, glass and the
   tall-screen padding are shared with them by class list above. What differs is
   the content: prose and citations rather than an image and a details grid, so
   the body is one readable column instead of two. */
.tin-info-modal {
    width: 100%;
    max-width: 640px;
    min-width: 0;
    max-height: calc(100vh - 100px);
    display: flex;
    flex-direction: column;
    border: 1px solid var(--color-accent);
    border-radius: 8px;
    box-shadow: 0 0 10px var(--glow-accent), 0 8px 24px rgba(0, 0, 0, 0.6);
    overflow: hidden;
}
.tin-info-modal .tin-im-head {
    display: flex;
    align-items: center;
    gap: 0.6rem;
    padding: 1rem 1rem 0.75rem;
}
.tin-info-modal .tin-im-title {
    flex: 1;
    min-width: 0;
    color: var(--text-primary, #fff);
    font-size: 16px;
    font-weight: 700;
}
.tin-info-modal .tin-im-close {
    width: 28px;
    height: 28px;
    flex-shrink: 0;
    display: flex;
    align-items: center;
    justify-content: center;
    border: 0;
    border-radius: 50%;
    background: rgba(211, 211, 211, 1);
    color: rgba(20, 20, 20, 0.95);
    font-size: 12px;
    line-height: 1;
    cursor: pointer;
}
.tin-info-modal .tin-im-close:hover { background: var(--color-danger); color: #fff; }
.tin-info-modal .tin-im-underline {
    height: 1px;
    margin: 0 1rem;
    background: rgba(128, 128, 128, 0.5);
}
.tin-info-modal .tin-im-body { overflow-y: auto; padding: 1rem; }
.tin-im-section + .tin-im-section { margin-top: 1rem; }
.tin-im-section-title {
    margin-bottom: 0.3rem;
    font-size: 11px;
    font-weight: 700;
    letter-spacing: 0.08em;
    text-transform: uppercase;
    color: var(--color-accent, #ffd84b);
}
.tin-im-text {
    margin: 0;
    font-size: 13px;
    line-height: 1.6;
    color: var(--text-bright, rgba(255, 255, 255, 0.85));
}
/* Citations: smaller and quieter than the prose, but selectable — the point of
   printing them in full is that they can be looked up. */
.tin-im-refs {
    margin: 0;
    padding-left: 1.2rem;
    font-size: 11.5px;
    line-height: 1.55;
    color: var(--text-muted, rgba(255, 255, 255, 0.55));
}
.tin-im-refs li + li { margin-top: 0.4rem; }
.tin-im-note {
    margin: 1.1rem 0 0;
    padding: 0.6rem 0.7rem;
    border: 1px solid rgba(244, 67, 54, 0.3);
    border-radius: 4px;
    background: rgba(244, 67, 54, 0.07);
    font-size: 11.5px;
    line-height: 1.55;
    color: var(--text-muted, rgba(255, 255, 255, 0.6));
}

/* The app-wide notch card shares the sound cards' box so it lines up in the
   grid, but it is not selectable — its whole action is the toggle at the foot. */
.tin-notch-card {
    display: flex;
    flex-direction: column;
    gap: 0.5rem;
    background: var(--input-bg, rgba(17,17,17,0.8));
    border: 1px solid var(--border-accent-light, rgba(255,216,75,0.2));
    border-radius: 4px;
    padding: 0.9rem;
    transition: border-color 0.15s ease, box-shadow 0.15s ease;
}
.tin-notch-card.tin-notch-on {
    border-color: #dc3545;
    box-shadow: 0 0 8px rgba(220,53,69,0.3), inset 0 0 12px rgba(220,53,69,0.08);
}
.tin-notch-card .tin-sound-desc { flex: 1; }
.tin-notch-state {
    font-size: 11px;
    font-weight: 600;
    color: var(--text-muted, rgba(255,255,255,0.55));
}
.tin-notch-card.tin-notch-on .tin-notch-state { color: #dc3545; }
.tin-notch-card.tin-notch-on .tin-sound-name { color: #dc3545; }
/* Full width, unlike the cards' preview button — it is the card's whole point. */
.tin-notch-toggle {
    width: 100%;
    min-width: 0;
    padding: 0.4rem 0.8rem;
    font-size: 12px;
}

.tin-sound-card {
    display: flex;
    flex-direction: column;
    gap: 0.5rem;
    background: var(--input-bg, rgba(17,17,17,0.8));
    border: 1px solid var(--border-accent-light, rgba(255,216,75,0.2));
    border-radius: 4px;
    padding: 0.9rem;
    cursor: pointer;
    /* No transform here: hovering a card must not shift it. The border brightens
       instead, so the row stays put under the pointer. */
    transition: border-color 0.15s ease, box-shadow 0.15s ease;
}
.tin-sound-card:hover {
    border-color: var(--color-accent, #ffd84b);
}
.tin-sound-card.selected {
    border-color: var(--color-accent, #ffd84b);
    box-shadow: 0 0 8px var(--glow-accent, rgba(255,216,75,0.3)), inset 0 0 12px var(--bg-accent-light, rgba(255,216,75,0.1));
}
.tin-sound-name {
    font-size: 13px;
    font-weight: 700;
    color: var(--text-bright, rgba(255,255,255,0.85));
}
.tin-sound-card.selected .tin-sound-name { color: var(--color-accent, #ffd84b); }
/* The notch card's title carries the warning sign, so it is a row rather than a
   line of text. */
#tin-global-notch-card .tin-sound-name {
    display: flex;
    align-items: center;
    gap: 0.4rem;
}
#tin-global-notch-card .bws-notch-sign { vertical-align: 0; }
.tin-sound-desc {
    font-size: 11.5px;
    line-height: 1.5;
    color: var(--text-muted, rgba(255,255,255,0.6));
    flex: 1;
}
/* Spectrum sketch: what the card does to the sound, drawn from the profile.
   The path is stroked, the box gives it a baseline to read against. */
.tin-sound-curve {
    height: 34px;
    border-radius: 3px;
    background: var(--bg-dark-light, rgba(0,0,0,0.25));
    border: 1px solid var(--border-accent-light, rgba(255,216,75,0.12));
    overflow: hidden;
}
.tin-sound-curve svg { display: block; width: 100%; height: 100%; }
.tin-sound-curve path {
    fill: none;
    stroke: var(--text-muted, rgba(255,255,255,0.55));
    stroke-width: 1.6;
    vector-effect: non-scaling-stroke;
}
.tin-sound-card.selected .tin-sound-curve path { stroke: var(--color-accent, #ffd84b); }

.tin-sound-tags { display: flex; gap: 0.4rem; }
.tin-tag {
    font-size: 10px;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.04em;
    padding: 0.15rem 0.5rem;
    border-radius: 3px;
    background: var(--bg-dark-medium, rgba(0,0,0,0.4));
    border: 1px solid var(--border-accent-light, rgba(255,216,75,0.2));
    color: var(--text-muted, rgba(255,255,255,0.6));
}
.tin-tag-rec {
    background: rgba(40, 167, 69, 0.18);
    border-color: rgba(40, 167, 69, 0.5);
    color: #28a745;
}
/* A .bws-button-a, so previewing paints the same accent → red as a step-2 play
   button, but sized down to sit inside a sound card: it keeps its own padding
   and font size, and overrides the 120px minimum the class carries. */
.tin-card-preview {
    align-self: flex-start;
    padding: 0.35rem 0.8rem;
    font-size: 12px;
    gap: 6px;
    /* "Stop" is shorter than "Preview", and the button is align-self: flex-start,
       so it would shrink the moment a preview started. Reserve the wider label's
       width — in em, so it follows the font size — and only the icon and the
       word change. */
    min-width: 8em;
}

/* ── Session ─────────────────────────────────────────────────────────────── */
.tin-profile-summary {
    font-size: 13px;
    font-weight: 600;
    color: var(--color-accent, #ffd84b);
}
.tin-dur-row { gap: 0.5rem; }
.tin-dur-btn {
    padding: 0.35rem 0.85rem;
    border-radius: 4px;
    background: var(--input-bg, rgba(17,17,17,0.8));
    border: 1px solid var(--border-accent-light, rgba(255,216,75,0.2));
    color: var(--text-muted, rgba(255,255,255,0.6));
    font-size: 12px;
    cursor: pointer;
    transition: all 0.15s ease;
}
.tin-dur-btn:hover { border-color: var(--color-accent, #ffd84b); color: var(--text-bright, rgba(255,255,255,0.85)); }
.tin-dur-btn.active {
    background: var(--bg-accent-medium, rgba(255,216,75,0.2));
    border-color: var(--color-accent, #ffd84b);
    color: var(--color-accent, #ffd84b);
    font-weight: 600;
}
.tin-session-bar { margin-top: 0.25rem; }
.tin-restart-btn {
    padding: 0.4rem 0.9rem;
    font-size: 12px;
}
.tin-restart-btn:hover { color: #dc3545; border-color: #dc3545; background: rgba(220,53,69,0.08); }

/* ── Notch width ─────────────────────────────────────────────────────────── */
.tin-notch-row { gap: 0.5rem; }
.tin-notch-btn {
    padding: 0.35rem 0.85rem;
    border-radius: 4px;
    background: var(--input-bg, rgba(17,17,17,0.8));
    border: 1px solid var(--border-accent-light, rgba(255,216,75,0.2));
    color: var(--text-muted, rgba(255,255,255,0.6));
    font-size: 12px;
    cursor: pointer;
    transition: all 0.15s ease;
}
.tin-notch-btn:hover { border-color: var(--color-accent, #ffd84b); color: var(--text-bright, rgba(255,255,255,0.85)); }
.tin-notch-btn.active {
    background: var(--bg-accent-medium, rgba(255,216,75,0.2));
    border-color: var(--color-accent, #ffd84b);
    color: var(--color-accent, #ffd84b);
    font-weight: 600;
}
/* A/B toggle: lit while the notch is bypassed, so the odd state is the obvious one */
#tin-bypass-notch.active {
    border-color: var(--color-accent, #ffd84b);
    color: var(--color-accent, #ffd84b);
    background: var(--bg-accent-light, rgba(255,216,75,0.1));
}

/* ── Listening log ───────────────────────────────────────────────────────── */
/* Where the session stands: the matched profile on top, the listening log under
   it, in one box. The chrome lives here rather than on either half so the two
   read as one panel — and because the log's own element is rewritten wholesale
   on every render, which is also why the profile line is its sibling and not its
   child. */
/* One line, and the box is only as tall as that line: what was matched on the
   left, how much has been listened to on the right, the week's bars after the
   figures rather than under them. The two halves wrap onto separate lines before
   either has to be squeezed — the profile line is a sentence and the empty-log
   message is a longer one. */
.tin-status-box {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: space-between;
    gap: 0.4rem 1.25rem;
    padding: 0.5rem 0.85rem;
    border: 1px solid var(--border-accent-light, rgba(255,216,75,0.2));
    border-radius: 4px;
    background: var(--bg-dark-lighter, rgba(0,0,0,0.1));
}
.tin-status-box .tin-profile-summary { flex: 1 1 240px; min-width: 0; }

/* ── Octave check ────────────────────────────────────────────────────────── */
.tin-octave-check { gap: 0.4rem; align-items: center; }

@media (max-width: 768px) {
    .tin-freq-readout { font-size: 18px; }
    .tin-sound-grid { grid-template-columns: 1fr 1fr; }
}
@media (max-width: 480px) {
    .tin-sound-grid { grid-template-columns: 1fr; }
    .tin-volume-inline { min-width: 150px; }
}

/* ═══════════════════════════════════════════════════════════════════════════
   BREATHWORK MODULE (brw-)
   Technique cards → animated breathing pacer → sound cues → session transport
   ═══════════════════════════════════════════════════════════════════════════ */

/* ── Technique cards ─────────────────────────────────────────────────────── */
.brw-technique-grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(190px, 1fr));
    gap: 0.75rem;
}
.brw-technique-card {
    display: flex;
    flex-direction: column;
    gap: 0.35rem;
    background: var(--input-bg, rgba(17,17,17,0.8));
    border: 1px solid var(--border-accent-light, rgba(255,216,75,0.2));
    border-radius: 4px;
    padding: 0.8rem 0.9rem;
    cursor: pointer;
    transition: border-color 0.15s ease, box-shadow 0.15s ease, transform 0.15s ease;
}
.brw-technique-card:hover {
    border-color: var(--color-accent, #ffd84b);
    transform: translateY(-1px);
}
.brw-technique-card.selected {
    border-color: var(--color-accent, #ffd84b);
    box-shadow: 0 0 8px var(--glow-accent, rgba(255,216,75,0.3)), inset 0 0 12px var(--bg-accent-light, rgba(255,216,75,0.1));
}
.brw-technique-name {
    font-size: 13px;
    font-weight: 700;
    color: var(--text-bright, rgba(255,255,255,0.85));
}
.brw-technique-card.selected .brw-technique-name { color: var(--color-accent, #ffd84b); }
.brw-technique-pattern {
    font-size: 12px;
    font-weight: 600;
    color: var(--color-accent, #ffd84b);
    font-variant-numeric: tabular-nums;
    opacity: 0.85;
}
.brw-technique-desc {
    font-size: 11.5px;
    line-height: 1.5;
    color: var(--text-muted, rgba(255,255,255,0.6));
}

/* Program info cards (official + cloud programs) — binaural-style: cover left,
   ⓘ button top-right, title/description/tags right. Covers are placeholders. */
#brw-main-parent-container .brw-ocard {
    position: relative; display: flex; gap: 10px; min-height: 92px; cursor: pointer; overflow: hidden;
    background: var(--input-bg, rgba(17,17,17,0.8)); border: 1px solid var(--border-accent-light, rgba(255,216,75,0.2));
    border-radius: 4px; transition: border-color .15s ease, box-shadow .15s ease;
}
#brw-main-parent-container .brw-ocard:hover { border-color: var(--color-accent, #ffd84b); box-shadow: 0 0 8px var(--glow-accent, rgba(255,216,75,0.25)); }
#brw-main-parent-container .brw-ocard-cover { width: 72px; flex-shrink: 0; align-self: stretch; background: #111; }
#brw-main-parent-container .brw-ocard-cover--empty { background: linear-gradient(135deg, #1c1c1c, #111); }
#brw-main-parent-container .brw-ocard-body { flex: 1; min-width: 0; padding: 10px 10px 10px 0; display: flex; flex-direction: column; gap: 4px; }
#brw-main-parent-container .brw-ocard-top { display: flex; align-items: flex-start; gap: 6px; }
#brw-main-parent-container .brw-ocard-title { flex: 1; font-size: 13px; font-weight: 700; color: var(--text-bright, rgba(255,255,255,0.85)); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/* Slightly smaller than the shared .bws-info-badge; colours still come from it. */
#brw-main-parent-container .brw-info-btn { width: 22px; height: 22px; font-size: 14px; }
#brw-main-parent-container .brw-ocard-desc { font-size: 11.5px; color: var(--text-bright, rgba(255,255,255,0.8)); line-height: 1.45; display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; }
/* Program info modal cover placeholder (no cover art yet) */
.brw-modal-cover--empty { background: linear-gradient(135deg, #1a1a1a, #111); }

/* ── Custom phase editor (per-phase rows + guidance layers) ──────────────── */
.brw-custom-editor {
    display: flex;
    flex-direction: column;
    align-items: stretch;
    gap: 0.6rem;
    background: var(--bg-dark-lighter, rgba(0,0,0,0.1));
    border: 1px dashed var(--border-accent-light, rgba(255,216,75,0.2));
    border-radius: 4px;
    padding: 0.75rem 1rem;
}
/* The same editor renders in the Player and (unscoped — it mounts on body)
   inside the timer guidance modal */
.brw-editor-top {
    display: flex;
    align-items: center;
    gap: 0.6rem;
    flex-wrap: wrap;
}
.brw-tech-controls { display: flex; align-items: center; gap: 0.5rem; flex-wrap: wrap; }
/* Attribute dropdown triggers (buttons, not selects — they open .brw-select-pop) */
.brw-sel-trigger { flex: 0 0 auto; padding-top: 0; padding-bottom: 0; }
.brw-tech-trigger { width: 170px; height: 34px; font-size: 12px; }
.brw-attr-trigger { width: 148px; height: 32px; font-size: 12px; }
.brw-phase-rows { display: flex; flex-direction: column; gap: 0.45rem; text-align: left; }
.brw-phase-row {
    display: flex;
    align-items: center;
    gap: 0.55rem;
    flex-wrap: wrap;
    padding: 0.3rem 0;
    border-top: 1px solid var(--bg-dark-light, rgba(0,0,0,0.2));
}
.brw-phase-tag {
    flex: 0 0 34px;
    font-size: 10px;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.05em;
    color: var(--color-accent, #ffd84b);
}
.brw-phase-secs { width: 56px; height: 32px; text-align: center; padding: 0 4px; font-size: 13px; }
.brw-more-btn { padding: 0.25rem 0.6rem; font-size: 11px; }
.brw-attr-chips { display: inline-flex; align-items: center; gap: 0.3rem; flex-wrap: wrap; min-width: 0; }
.brw-chip-attr {
    display: inline-flex;
    align-items: center;
    gap: 4px;
    max-width: 100%;
    font-size: 11px;
    padding: 2px 8px;
    border-radius: 12px;
    background: var(--bg-accent-light, rgba(255,216,75,0.1));
    border: 1px solid var(--border-accent-light, rgba(255,216,75,0.2));
    color: var(--color-accent, #ffd84b);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}
.brw-chip-x {
    background: none;
    border: 0;
    padding: 0;
    cursor: pointer;
    font-size: 10px;
    color: inherit;
    opacity: 0.7;
    line-height: 1;
}
.brw-chip-x:hover { opacity: 1; }

/* Guidance popover (+ Guidance) — shares the ft dropdown look; must sit above
   the timer edit modal overlay (z-index 100000) */
.brw-guide-pop { z-index: 100001; }
.brw-guide-pop .brw-guide-body { overflow-y: auto; padding: 0.5rem 0.75rem 0.75rem; display: flex; flex-direction: column; gap: 0.6rem; }
.brw-guide-pop .brw-guide-row { display: flex; flex-direction: column; gap: 4px; }
.brw-guide-pop .brw-guide-label {
    font-size: 10px;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.05em;
    color: var(--text-muted, rgba(255,255,255,0.6));
}
.brw-guide-pop .brw-guide-trigger { width: 100%; height: 32px; font-size: 12px; }
.brw-guide-pop .brw-guide-text { width: 100%; height: 32px; text-align: left; padding: 0 8px; font-size: 12px; }

/* Generic select popup — stacks above the guidance popover (100001) */
.brw-select-pop { z-index: 100002; }
.brw-guide-pop .brw-guide-checks { display: flex; gap: 0.5rem; flex-wrap: wrap; }
.brw-guide-pop .brw-guide-check {
    display: inline-flex;
    align-items: center;
    gap: 4px;
    font-size: 12px;
    color: var(--text-muted, rgba(255,255,255,0.6));
    cursor: pointer;
}

/* Pacer guidance lines (route/focus/expand info + text cue) */
.brw-pacer-guide {
    font-size: 12px;
    color: var(--text-muted, rgba(255,255,255,0.6));
    text-align: center;
    min-height: 1.2em;
    margin-top: -0.4rem;
}
.brw-pacer-cue {
    font-size: 13px;
    font-style: italic;
    color: var(--color-accent, #ffd84b);
    text-align: center;
    min-height: 1.2em;
    margin-top: -0.6rem;
}

/* ── Animated pacer ──────────────────────────────────────────────────────── */
.brw-pacer-stage {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 0.9rem;
    padding: 1.25rem 0 0.5rem;
}
.brw-pacer-ring {
    position: relative;
    width: 240px;
    height: 240px;
    border-radius: 50%;
    border: 1px solid var(--border-accent-light, rgba(255,216,75,0.2));
    display: flex;
    align-items: center;
    justify-content: center;
    flex-shrink: 0;
}
.brw-pacer-circle {
    position: absolute;
    inset: 10px;
    border-radius: 50%;
    background: radial-gradient(circle at 50% 42%,
        rgba(255, 216, 75, 0.55) 0%,
        rgba(255, 216, 75, 0.28) 45%,
        rgba(255, 216, 75, 0.10) 75%,
        rgba(255, 216, 75, 0.04) 100%);
    box-shadow: 0 0 24px var(--glow-accent, rgba(255,216,75,0.3)),
                inset 0 0 18px rgba(255, 216, 75, 0.25);
    transform: scale(0.55);
    will-change: transform;
}
.brw-pacer-text {
    position: relative;
    z-index: 1;
    text-align: center;
    pointer-events: none;
}
.brw-phase-label {
    font-size: 18px;
    font-weight: 600;
    letter-spacing: 0.04em;
    color: var(--text-bright, rgba(255,255,255,0.9));
    text-shadow: 0 1px 6px rgba(0,0,0,0.55);
}
.brw-phase-count {
    margin-top: 0.2rem;
    font-size: 30px;
    font-weight: 700;
    font-variant-numeric: tabular-nums;
    color: var(--color-accent, #ffd84b);
    text-shadow: 0 1px 6px rgba(0,0,0,0.55);
    min-height: 36px;
}
.brw-cycle-count {
    font-size: 12px;
    color: var(--text-muted, rgba(255,255,255,0.6));
}

/* ── Chips (cues + duration) ─────────────────────────────────────────────── */
.brw-row {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    flex-wrap: wrap;
}
.brw-chip {
    padding: 0.35rem 0.85rem;
    border-radius: 4px;
    background: var(--input-bg, rgba(17,17,17,0.8));
    border: 1px solid var(--border-accent-light, rgba(255,216,75,0.2));
    color: var(--text-muted, rgba(255,255,255,0.6));
    font-size: 12px;
    cursor: pointer;
    transition: all 0.15s ease;
    white-space: nowrap;
}
.brw-chip:hover { border-color: var(--color-accent, #ffd84b); color: var(--text-bright, rgba(255,255,255,0.85)); }
.brw-chip.active {
    background: var(--bg-accent-medium, rgba(255,216,75,0.2));
    border-color: var(--color-accent, #ffd84b);
    color: var(--color-accent, #ffd84b);
    font-weight: 600;
}
.brw-session-bar { margin-top: 0.25rem; }

@media (max-width: 768px) {
    .brw-technique-grid { grid-template-columns: 1fr 1fr; }
    .brw-pacer-ring { width: 200px; height: 200px; }
}
@media (max-width: 480px) {
    .brw-technique-grid { grid-template-columns: 1fr; }
    .brw-pacer-ring { width: 170px; height: 170px; }
    .brw-phase-label { font-size: 15px; }
    .brw-phase-count { font-size: 24px; }
}

/* ═══════════════════════════════════════════════════════════════════════════
   BREATHWORK MODULE v2 (brw-) — Presets / Player / Timers layout
   ═══════════════════════════════════════════════════════════════════════════ */

/* ── Preset tabs (mirrors the fp-/nat- preset tab bars) ──────────────────── */
/* Same sizing as the shared preset tab bars (asmr/fp/nat/…): full-width tabs */
#brw-main-parent-container .brw-tabs { display: flex; gap: 8px; margin-bottom: 1rem; }
#brw-main-parent-container .brw-tab {
    flex: 1;
    padding: 0.6rem 1rem;
    border-radius: 8px;
    cursor: pointer;
    background: var(--bg-dark-lighter);
    color: var(--text-muted);
    border: 1px solid var(--border-accent-light);
    font-size: 14px;
    font-weight: 500;
    transition: background .2s ease, color .2s ease, border-color .2s ease;
}
#brw-main-parent-container .brw-tab:hover { border-color: var(--color-accent, #ffd84b); color: var(--text-bright, rgba(255,255,255,0.85)); }
#brw-main-parent-container .brw-tab.active { background: var(--color-accent); color: #111; border-color: var(--color-accent); }

.brw-save-btn {
    padding: 9px 16px;
    border-radius: 6px;
    cursor: pointer;
    font-size: 13px;
    font-weight: 700;
    border: 1px solid var(--color-accent, #ffd84b);
    background: var(--color-accent, #ffd84b);
    color: #000;
}
.brw-save-btn:hover { background: #fff; }

#brw-main-parent-container .brw-grid-msg {
    grid-column: 1 / -1;
    text-align: center;
    color: var(--text-muted, rgba(255,255,255,0.6));
    padding: 1.5rem 1rem;
    font-size: 13px;
    line-height: 1.6;
}

#brw-main-parent-container .brw-group-subhead {
    margin: .5rem 0;
    font-size: 13px;
    font-weight: 700;
    letter-spacing: .06em;
    text-transform: uppercase;
    color: var(--text-muted, rgba(255,255,255,0.6));
}

/* Personal preset delete — small ✕ in the card corner */
#brw-main-parent-container .brw-technique-card { position: relative; }
#brw-main-parent-container .brw-personal-del {
    position: absolute;
    top: 6px;
    right: 6px;
    width: 20px;
    height: 20px;
    border-radius: 50%;
    border: 0;
    cursor: pointer;
    background: rgba(211,211,211,0.9);
    color: #141414;
    font-size: 11px;
    line-height: 1;
    padding: 0;
    transition: all 0.15s ease;
}
#brw-main-parent-container .brw-personal-del:hover { background: #dc3545; color: #fff; }

/* ── Player: timers-program note ─────────────────────────────────────────── */
#brw-main-parent-container .brw-timers-note {
    font-size: 12px;
    font-weight: 600;
    color: var(--color-accent, #ffd84b);
    background: var(--bg-accent-light, rgba(255,216,75,0.1));
    border: 1px solid var(--border-accent-light, rgba(255,216,75,0.2));
    border-radius: 4px;
    padding: 0.5rem 0.9rem;
}

/* ── Segmented session seek (same look as the timer modules) ─────────────── */
#brw-main-parent-container .ft-seek-wrap { position: relative; flex: 3; min-width: 0; display: flex; align-items: center; }
#brw-main-parent-container .ft-seek-wrap .hpp-seek { flex: 1; width: auto; position: relative; z-index: 1; background: transparent !important; }
#brw-main-parent-container .ft-seek-segs { position: absolute; left: 0; right: 0; top: 50%; height: 6px; transform: translateY(-50%); display: flex; align-items: center; gap: 1px; pointer-events: none; }

/* ── Timer rows ──────────────────────────────────────────────────────────── */
#brw-main-parent-container #brw-timers { display: flex; flex-direction: column; gap: 0.75rem; }
#brw-main-parent-container .brw-timer {
    display: flex;
    align-items: center;
    gap: 0.9rem;
    flex-wrap: wrap;
    padding: 0.7rem 0.9rem;
    background: var(--bg-dark-light, rgba(0,0,0,0.2));
    border: 1px solid var(--border-accent-light, rgba(255,216,75,0.2));
    border-radius: 4px;
    transition: border-color 0.2s ease, box-shadow 0.2s ease;
}
#brw-main-parent-container .brw-timer.brw-timer-active {
    border-color: var(--color-accent, #ffd84b);
    box-shadow: 0 0 8px var(--glow-accent, rgba(255,216,75,0.3));
}
#brw-main-parent-container .brw-timer-name {
    font-size: 13px;
    font-weight: 700;
    color: var(--color-accent, #ffd84b);
    white-space: nowrap;
    min-width: 62px;
}
/* Fixed width so rows don't reflow when preset names of different lengths are picked */
#brw-main-parent-container .brw-timer-preset { width: 190px; min-width: 0; flex: 0 0 190px; }
#brw-main-parent-container .brw-timer-phases { display: flex; align-items: center; gap: 0.7rem; flex-wrap: wrap; }
#brw-main-parent-container .brw-tp {
    display: flex;
    align-items: center;
    gap: 0.35rem;
    font-size: 10px;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.04em;
    color: var(--text-muted, rgba(255,255,255,0.6));
    white-space: nowrap;
}
#brw-main-parent-container .brw-tp input { width: 56px; height: 32px; text-align: center; padding: 0 4px; font-size: 13px; }
#brw-main-parent-container .brw-duration { display: flex; align-items: center; gap: 4px; margin-left: auto; }
#brw-main-parent-container .brw-dur-input { width: 44px; height: 34px; text-align: center; padding: 0 4px; font-size: 13px; }
#brw-main-parent-container .brw-dur-colon { color: var(--text-muted, rgba(255,255,255,0.6)); font-weight: 600; }

/* Timer remove — grey circle with dark X, red with white X on hover (matches ft-remove) */
#brw-main-parent-container .brw-timer-edit {
    width: 26px; height: 26px; flex-shrink: 0; border-radius: 50%; cursor: pointer; padding: 0;
    display: inline-flex; align-items: center; justify-content: center;
    background: var(--input-bg, rgba(17,17,17,0.8));
    border: 1px solid var(--border-accent-light, rgba(255,216,75,0.2));
    color: var(--text-muted, rgba(255,255,255,0.6));
    font-size: 14px; line-height: 1;
    transition: all 0.2s ease;
}
#brw-main-parent-container .brw-timer-edit:hover {
    border-color: var(--color-accent, #ffd84b);
    color: var(--color-accent, #ffd84b);
    transform: scale(1.08);
}
#brw-main-parent-container .brw-timer-edit.has-guidance {
    color: var(--color-accent, #ffd84b);
    border-color: var(--color-accent, #ffd84b);
}
#brw-main-parent-container .brw-remove {
    width: 26px; height: 26px; flex-shrink: 0; border-radius: 50%; border: 0; cursor: pointer; padding: 0;
    background: rgba(211,211,211,1) url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' width='24' height='24'%3E%3Cpath d='M18 6L6 18M6 6l12 12' stroke='%23141414' stroke-width='2.5' stroke-linecap='round'/%3E%3C/svg%3E") center/13px no-repeat;
    transition: all 0.2s ease;
}
#brw-main-parent-container .brw-remove:hover {
    background: #dc3545 url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' width='24' height='24'%3E%3Cpath d='M18 6L6 18M6 6l12 12' stroke='%23FFFFFF' stroke-width='2.5' stroke-linecap='round'/%3E%3C/svg%3E") center/13px no-repeat;
    transform: scale(1.08);
}

#brw-main-parent-container .brw-timer-actions { display: flex; gap: 0.75rem; flex-wrap: wrap; margin-top: 0.25rem; }
#brw-main-parent-container .brw-timer-actions .bws-main-button-style:disabled { opacity: 0.4; cursor: not-allowed; }

@media (max-width: 768px) {
    #brw-main-parent-container .brw-duration { margin-left: 0; }
    #brw-main-parent-container .brw-timer-preset { width: 150px; flex: 0 0 150px; }
}

/* ── Breathwork preset info modal (coverless variant of the fp-modal) ────── */
.brw-preset-modal { position: relative; }
.brw-preset-modal .fp-modal-body { padding-top: 18px; padding-right: 44px; }
.brw-modal-pattern {
    font-size: 24px;
    font-weight: 700;
    color: var(--color-accent, #ffd84b);
    font-variant-numeric: tabular-nums;
    margin: 6px 0 2px;
}
.brw-modal-meta {
    font-size: 12px;
    color: var(--text-muted, rgba(255,255,255,0.6));
    margin-bottom: 10px;
}
.brw-modal-guide {
    display: flex;
    flex-direction: column;
    gap: 4px;
    margin-top: 10px;
    padding: 8px 10px;
    background: var(--bg-dark-light, rgba(0,0,0,0.2));
    border: 1px dashed var(--border-accent-light, rgba(255,216,75,0.2));
    border-radius: 4px;
    font-size: 12px;
    color: var(--text-muted, rgba(255,255,255,0.6));
    text-align: left;
}
.brw-modal-guide-line { display: flex; gap: 8px; align-items: baseline; }
.brw-modal-guide-tag {
    flex: 0 0 32px;
    font-size: 10px;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.05em;
    color: var(--color-accent, #ffd84b);
}

/* Timer guidance modal — the shared phase editor inside an fp-modal */
.brw-edit-modal { position: relative; width: min(680px, 94vw); max-width: min(680px, 94vw); }
.brw-edit-modal .fp-modal-body { padding-top: 18px; padding-right: 44px; }
.brw-edit-modal .brw-editor-top { margin: 10px 0 4px; }
.brw-edit-modal .brw-edit-modal-actions { display: flex; justify-content: flex-end; margin-top: 14px; }

/* ── Breathwork dropdown triggers (same look as the FT preset trigger; those
      rules are scoped to #ft-main-parent-container, so copy them here). Used by
      the pattern picker AND every attribute dropdown — also inside the timer
      guidance modal and the guidance popover, which mount on <body>. ── */
#brw-main-parent-container .ft-preset-trigger,
.brw-edit-modal .ft-preset-trigger,
.brw-guide-pop .ft-preset-trigger {
    display: flex; align-items: center; text-align: left; cursor: pointer;
    color: var(--text-muted);
}
#brw-main-parent-container .ft-preset-trigger.has-value,
.brw-edit-modal .ft-preset-trigger.has-value,
.brw-guide-pop .ft-preset-trigger.has-value { color: var(--color-accent); }
#brw-main-parent-container .ft-preset-trigger .ft-preset-trigger-text,
.brw-edit-modal .ft-preset-trigger .ft-preset-trigger-text,
.brw-guide-pop .ft-preset-trigger .ft-preset-trigger-text {
    flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}

/* ═══════════════════════════════════════════════════════════════════════════
   PRESETS BUILDER MODULE (PRESETS-BUILDER-MODULE/) — prb-*
   ═══════════════════════════════════════════════════════════════════════════ */

/* ── "Personal" tab glyph (shared) ────────────────────────────────
   Every module's Personal tab (packs, playlists, tracks, presets) carries the
   same filled cloud — the closed twin of the "save to the cloud" mark on the
   cards — so "this lives in your cloud" reads the same everywhere. One pair of
   rules serves all the per-module tab families (.prb-tab, .alb-tab, .bp-tab,
   .fp-tab, .ip-tab, .brw-tab, .hcp-tab, .gcp-tab, .dcp-tab, .scp-tab,
   .av2p-tab); the button opts in with .bws-tabicon-btn. The glyph inherits
   currentColor, so it turns black on the active accent tab by itself. */
.bws-tabicon-btn { display: inline-flex; align-items: center; justify-content: center; gap: 8px; }
.bws-tab-cloud   { flex: none; }

/* Tabs (same pattern as the playlist modules) */
#prb-main-parent-container .prb-tabs { display: flex; gap: 8px; margin-bottom: 1rem; }
#prb-main-parent-container .prb-tab {
    flex: 1; padding: 0.6rem 1rem; border-radius: 8px; cursor: pointer;
    background: var(--bg-dark-lighter, #1a1a1a); color: var(--text-muted, #aaa);
    border: 1px solid var(--border-accent-light, rgba(255,216,75,0.25));
    font-size: 14px; font-weight: 500;
    transition: background .2s ease, color .2s ease, border-color .2s ease;
}
#prb-main-parent-container .prb-tab.active { background: var(--color-accent, #ffd84b); color: #111; border-color: var(--color-accent, #ffd84b); }

/* ── Filter / search bar (title / tags / date / duration) ─────────────────
   Same fields and look as the media library's filter bar. Unscoped and shared:
   the pack builder and the five playlist modules' Personal tabs wear the same
   bar, so a change here reaches all of them. */
.bws-fbar {
    display: flex;
    flex-wrap: wrap;
    align-items: flex-end;
    gap: 10px;
    width: 100%;
    box-sizing: border-box;
    padding: 12px;
    margin-bottom: 1rem;
    border-radius: 8px;
    background: var(--bg-dark-lighter, #1a1a1a);
    border: 1px solid var(--border-accent-light, rgba(255,216,75,0.25));
}
.bws-fbar-field { display: flex; flex-direction: column; gap: 4px; flex: 1; min-width: 150px; }
.bws-fbar-field--range { flex: 1.4; min-width: 210px; }
/* The tags box in the instrument bars earns more of the row than the
   dropdowns beside it — a tag list is longer than "All types". */
.bws-fbar-field--wide { flex: 2; min-width: 180px; }
.bws-fbar-field label {
    font-size: 10px; font-weight: 700; letter-spacing: 0.06em;
    text-transform: uppercase; color: var(--text-muted, #aaa);
}
.bws-fbar-input {
    width: 100%; box-sizing: border-box; padding: 8px 9px; border-radius: 5px;
    border: 1px solid var(--border-accent-light, rgba(255,216,75,0.25));
    background: var(--bg-dark-light, #141414); color: #fff;
    font-size: 13px; font-family: inherit;
}
.bws-fbar-input:focus { outline: none; border-color: var(--color-accent, #ffd84b); }
.bws-fbar-rangewrap { display: flex; align-items: center; gap: 6px; }
.bws-fbar-rangewrap .bws-fbar-input { flex: 1; min-width: 0; }
.bws-fbar-num { -moz-appearance: textfield; }
.bws-fbar-dash { color: var(--text-muted, #888); flex: 0 0 auto; }
.bws-fbar-clear {
    flex: 0 0 auto; padding: 8px 14px; border-radius: 5px; cursor: pointer;
    font-size: 12px; font-weight: 700; font-family: inherit;
    border: 1px solid var(--border-accent-light, rgba(255,216,75,0.25));
    background: var(--bg-dark-light, #141414); color: var(--text-muted, #aaa);
}
.bws-fbar-clear:hover { border-color: var(--color-accent, #ffd84b); color: var(--color-accent, #ffd84b); }

/* ── Favorites star (js/bws-favorites.js) ─────────────────────────────────────
   ON THE CARD. Pinned to the card's own top-left corner rather than to the
   cover element, which is what makes one rule serve both card layouts: in the
   compact view the cover is the 84px strip down the left, in the large view it
   is the band across the top, and the top-left corner is over the cover either
   way. Every card shell in the app is already position:relative.

   The disc behind the glyph is not decoration — the covers are photographs, and
   a bare outline star disappears over a pale one. */
.bws-fav-star {
    position: absolute; top: 6px; left: 6px; z-index: 3;
    display: flex; align-items: center; justify-content: center;
    width: 26px; height: 26px; padding: 0; border-radius: 50%; cursor: pointer;
    background: rgba(0, 0, 0, 0.55);
    border: 1px solid rgba(255, 255, 255, 0.22);
    color: #fff;
    transition: background-color .15s ease, border-color .15s ease, color .15s ease, box-shadow .15s ease;
}
/* Outline until it is on, then filled — the whole point of the control, so it
   is a fill change and not merely a colour change. */
.bws-fav-star svg path { fill: none; stroke: currentColor; stroke-width: 1.8; stroke-linejoin: round; }
.bws-fav-star:hover {
    border-color: var(--color-accent, #ffd84b); color: var(--color-accent, #ffd84b);
    box-shadow: 0 0 0 2px rgba(255, 216, 75, 0.35);
}
.bws-fav-star.is-on { color: var(--color-accent, #ffd84b); border-color: var(--color-accent, #ffd84b); }
.bws-fav-star.is-on svg path { fill: currentColor; }
.bws-fav-star:focus-visible { outline: 2px solid var(--color-accent, #ffd84b); outline-offset: 2px; }

/* IN THE FILTER BAR. Same shell as .bws-fbar-clear so the two read as a pair at
   the end of the bar, with the star carrying the meaning. */
.bws-fbar-fav {
    flex: 0 0 auto; display: flex; align-items: center; gap: 6px;
    padding: 8px 12px; border-radius: 5px; cursor: pointer;
    font-size: 12px; font-weight: 700; font-family: inherit;
    border: 1px solid var(--border-accent-light, rgba(255,216,75,0.25));
    background: var(--bg-dark-light, #141414); color: var(--text-muted, #aaa);
}
.bws-fbar-fav svg path { fill: none; stroke: currentColor; stroke-width: 1.8; stroke-linejoin: round; }
.bws-fbar-fav:hover { border-color: var(--color-accent, #ffd84b); color: var(--color-accent, #ffd84b); }
.bws-fbar-fav.is-active {
    background: var(--color-accent, #ffd84b); border-color: var(--color-accent, #ffd84b); color: #000;
}
.bws-fbar-fav.is-active svg path { fill: currentColor; }
.bws-fbar-fav.is-active:hover { filter: brightness(1.08); color: #000; }

/* A signed-out visitor clicking a star is sent to the sign-in button that is
   always in the header; this is the two seconds of "over here" that makes the
   scroll legible. */
.bws-auth-attention { animation: bws-auth-attention-pulse 0.8s ease-in-out 3; border-radius: 6px; }
@keyframes bws-auth-attention-pulse {
    0%, 100% { box-shadow: 0 0 0 0 rgba(255, 216, 75, 0); }
    50%      { box-shadow: 0 0 0 4px rgba(255, 216, 75, 0.55); }
}
@media (prefers-reduced-motion: reduce) {
    .bws-auth-attention { animation: none; box-shadow: 0 0 0 3px rgba(255, 216, 75, 0.55); }
}

/* ── Card layout switch (compact / large) ─────────────────────────────────
   Two icons in their own little panel to the left of the filter bar, wearing
   the bar's own background and border so the pair reads as one strip. The
   buttons are written by js/bws-card-view.js; it also puts
   `data-bws-cardview="side|top"` on the module container, which is what the
   two layout blocks below hang off. */
.bws-fbar-row { display: flex; align-items: stretch; gap: 10px; width: 100%; margin-bottom: 1rem; }
.bws-fbar-row > .bws-fbar { flex: 1; min-width: 0; margin-bottom: 0; }
/* The media library keeps its own .mlib-fbar rather than the shared .bws-fbar,
   so it needs the same flex treatment by name. margin-top is the 0.6rem the bar
   carries to clear the tabs on its own; inside the row that space belongs to the
   row, which .bbp-preset-control's gap already provides. */
.bws-fbar-row > .mlib-fbar { flex: 1; min-width: 0; margin-top: 0; margin-bottom: 0; }
.bws-viewtoggle {
    flex: 0 0 auto; display: flex; align-items: center; gap: 6px;
    box-sizing: border-box; padding: 12px; border-radius: 8px;
    background: var(--bg-dark-lighter, #1a1a1a);
    border: 1px solid var(--border-accent-light, rgba(255,216,75,0.25));
}
.bws-viewtoggle-btn {
    display: flex; align-items: center; justify-content: center;
    width: 34px; height: 34px; padding: 0; border-radius: 5px; cursor: pointer;
    border: 1px solid var(--border-accent-light, rgba(255,216,75,0.25));
    background: var(--bg-dark-light, #141414); color: var(--text-muted, #aaa);
    transition: background-color .15s ease, border-color .15s ease, color .15s ease;
}
.bws-viewtoggle-btn:hover { border-color: var(--color-accent, #ffd84b); color: var(--color-accent, #ffd84b); }
.bws-viewtoggle-btn.active { background: var(--color-accent, #ffd84b); border-color: var(--color-accent, #ffd84b); color: #111; }
@media (max-width: 560px) { .bws-fbar-row { flex-wrap: wrap; } }

/* ── LARGE view: the album card stacks, cover on top ──────────────────────
   The per-module .alb-card rules are id-scoped duplicates, so this has to be
   id-scoped too in order to win. Propagating the switch to another album
   module = adding its container id to these four selector lists. All five
   album modules are in them; a sixth would go here. */
#medm-main-parent-container[data-bws-cardview="top"] .alb-card,
#asmr-main-parent-container[data-bws-cardview="top"] .alb-card,
#gm-main-parent-container[data-bws-cardview="top"] .alb-card,
#nat-main-parent-container[data-bws-cardview="top"] .alb-card,
#spc-main-parent-container[data-bws-cardview="top"] .alb-card {
    flex-direction: column; gap: 0; min-height: 220px;
}
/* 16:9 rather than a fixed band — see the note on .plp-pcard-cover. */
#medm-main-parent-container[data-bws-cardview="top"] .alb-card-cover,
#asmr-main-parent-container[data-bws-cardview="top"] .alb-card-cover,
#gm-main-parent-container[data-bws-cardview="top"] .alb-card-cover,
#nat-main-parent-container[data-bws-cardview="top"] .alb-card-cover,
#spc-main-parent-container[data-bws-cardview="top"] .alb-card-cover {
    width: 100%; height: auto; aspect-ratio: 16 / 9; align-self: auto;
}
#medm-main-parent-container[data-bws-cardview="top"] .alb-card-body,
#asmr-main-parent-container[data-bws-cardview="top"] .alb-card-body,
#gm-main-parent-container[data-bws-cardview="top"] .alb-card-body,
#nat-main-parent-container[data-bws-cardview="top"] .alb-card-body,
#spc-main-parent-container[data-bws-cardview="top"] .alb-card-body {
    padding: 10px 12px 12px;
}
#medm-main-parent-container[data-bws-cardview="top"] .alb-card-empty,
#asmr-main-parent-container[data-bws-cardview="top"] .alb-card-empty,
#gm-main-parent-container[data-bws-cardview="top"] .alb-card-empty,
#nat-main-parent-container[data-bws-cardview="top"] .alb-card-empty,
#spc-main-parent-container[data-bws-cardview="top"] .alb-card-empty {
    min-height: 220px;
}

/* The same large view for the media library's track grid. Its cards are
   .mlib-track-card / .mlib-rc-* rather than .alb-*, and the compact shape is
   their default, so only the stacked view is spelled out here. The pinned
   "Upload a Song" entry card wears the same classes and comes along with it. */
#mlib-main-parent-container[data-bws-cardview="top"] .mlib-track-card {
    flex-direction: column; align-items: stretch; min-height: 220px;
}
/* 16:9 rather than a fixed band — see the note on .plp-pcard-cover. height:auto
   is required: a definite height (the 96px strip) wins over aspect-ratio. */
#mlib-main-parent-container[data-bws-cardview="top"] .mlib-rc-cover {
    width: 100%; flex: 0 0 auto; height: auto; aspect-ratio: 16 / 9; align-self: auto;
}
#mlib-main-parent-container[data-bws-cardview="top"] .mlib-rc-body {
    padding: 10px 12px 12px;
}

/* The same large view for the three timer preset catalogs. Their .fp-/.bp-/.ip-
   card rules are id-scoped duplicates of the album card, so these have to be
   id-scoped too; only the class prefix differs from the block above. The entry
   tiles come along on their own — they wear the same .X-card-cover. */
#fp-presets-parent-container[data-bws-cardview="top"] .fp-card,
#bp-presets-parent-container[data-bws-cardview="top"] .bp-card,
#ip-presets-parent-container[data-bws-cardview="top"] .ip-card {
    flex-direction: column; gap: 0; min-height: 220px;
}
/* 16:9 rather than a fixed band — see the note on .plp-pcard-cover. */
#fp-presets-parent-container[data-bws-cardview="top"] .fp-card-cover,
#bp-presets-parent-container[data-bws-cardview="top"] .bp-card-cover,
#ip-presets-parent-container[data-bws-cardview="top"] .ip-card-cover {
    width: 100%; height: auto; aspect-ratio: 16 / 9; align-self: auto;
}
#fp-presets-parent-container[data-bws-cardview="top"] .fp-card-body,
#bp-presets-parent-container[data-bws-cardview="top"] .bp-card-body,
#ip-presets-parent-container[data-bws-cardview="top"] .ip-card-body {
    padding: 10px 12px 12px;
}
#fp-presets-parent-container[data-bws-cardview="top"] .fp-card-empty,
#bp-presets-parent-container[data-bws-cardview="top"] .bp-card-empty,
#ip-presets-parent-container[data-bws-cardview="top"] .ip-card-empty {
    min-height: 220px;
}

/* ── COMPACT view: the personal playlist / track card lies down, cover left ─
   Mirror image of the block above, for the cards that stack by default. These
   are unscoped like the .plp-pcard rules they override, so they come along to
   every module that adopts the switch. */
[data-bws-cardview="side"] .plp-pcard { flex-direction: row; gap: 10px; min-height: 160px; }
/* aspect-ratio:auto undoes the 16:9 on the base rule: here the cover is an 84px
   strip down the side of the card, sized by the card's height, not by its own
   ratio. Without the reset it would resolve to 84x47 and leave a gap. */
[data-bws-cardview="side"] .plp-pcard-cover { flex: 0 0 84px; width: 84px; height: auto; aspect-ratio: auto; align-self: stretch; }
[data-bws-cardview="side"] .plp-pcard-body { flex: 1; min-width: 0; padding: 10px 10px 10px 0; }
[data-bws-cardview="side"] .plp-pcard-custom { min-height: 160px; }
[data-bws-cardview="side"] .plp-card-empty { min-height: 160px; }

/* Pack cards */
/* "Custom Pack" card — the builder modal's entry point. Same wide cover-left
   shape as every other pack card, with an empty cover placeholder and the
   shared yellow .bws-card-create-btn. */
/* Entry-card look/behaviour is shared with the three preset modules — see
   "Entry cards" further down. */

/* Filler slot on a short last page, so the 3x4 grid keeps its shape. Same look
   as .fp-card-empty in the three timer catalogs. */

/* Pager under each tab's grid — mirrors #fp-presets-parent-container .fp-pager. */

/* Pack card (BOTH tabs — Brainwavesounds and Personal) — binaural-style: cover
   thumbnail left, info button top-right, title/description/tags on the right.
   Mirrors #bp-presets .bp-card. Load/Delete live in the info modal, not the card. */
    /* .prb-info-btn look comes from .bws-info-badge */

/* Pack access-tier badges — same labels/colors as the timer catalogs' .fp-badge-*,
   and personal packs carry the same PERSONAL badge those modules' personal presets
   do. The shape comes from the shared .prb-badge base rule. Unscoped, because the
   pack info modal renders these outside #prb-main-parent-container. */
.prb-badge-free { background: rgba(75,200,100,0.25); }
.prb-badge-basic { background: rgba(75,150,255,0.25); }
.prb-badge-premium { background: rgba(180,75,255,0.25); }
.prb-badge-paid { background: rgba(255,216,75,0.18); }
.prb-badge-personal { background: rgba(255,255,255,0.14); }

/* Pack info modal reuses the shared fp-modal-* styles + the shared
   .bws-modal-pills row; only the tag chips are new. */
/* Shared info-modal tag chips (packs + timers) */
.bws-modal-tags { display: flex; flex-wrap: wrap; gap: 5px; margin: 0 0 12px; }
/* Chips sit below the description; the description carries no block margin of
   its own,
so the gap above the first chip row comes from here. One selector per
   modal flavour (packs/frequency,
   binaural,
isochronic). */
   .fp-modal-desc + .bws-modal-tags,
   .bp-modal-desc + .bws-modal-tags,
   .ip-modal-desc + .bws-modal-tags,
   .alb-modal-desc + .bws-modal-tags { margin-top: 12px; }
/* Album modal chips are clickable (they filter the grid); the timer/pack ones
   are not, so the affordance is scoped to the album flavour only. */
.alb-modal-tag { cursor: pointer; transition: background .15s ease, border-color .15s ease; }
.alb-modal-tag:hover { background: rgba(255,216,75,0.22); border-color: var(--color-accent, #ffd84b); }
/* .bws-modal-tag chip look is the shared .bws-card-tag — only the container's
   modal-specific margins live here. */
/* Placeholder tiles for cards/modals without cover art (personal presets) */
.bws-modal-cover--empty { background: linear-gradient(135deg, #1a1a1a, #111); }
.bws-card-cover--empty { background: linear-gradient(135deg, #1c1c1c, #111); }
/* Builder cards ("Custom … Builder") draw their cover instead of loading art:
   a black person glyph centred on the accent yellow. The per-module cover rules
   are id-scoped, so this has to be too in order to win the background. */
#fp-presets-parent-container .bws-card-cover--custom,
#bp-presets-parent-container .bws-card-cover--custom,
#ip-presets-parent-container .bws-card-cover--custom,
#singing-bowls-session-presets-container .bws-card-cover--custom,
.plp-pcard-custom .bws-card-cover--custom,
#prb-main-parent-container  .bws-card-cover--custom {
    display: flex; align-items: center; justify-content: center; overflow: hidden;
    background: var(--color-accent, #ffd84b);
}
.bws-card-cover-icon { width: 52px; height: 52px; }

/* ── Entry cards ──────────────────────────────────────────────────────────
   The "create/save something" tiles pinned to the front of the Personal grids
   (packs, frequency, binaural, isochronic). Each card is one big button: the
   whole tile is clickable, and its two yellow parts — the glyph cover and the
   action button — rest in a darker gold and brighten to the shared accent
   together on hover. The button is inert so it can never take the hover or the
   focus on its own; the card carries role="button" and the key handler. */
#fp-presets-parent-container .fp-card-custom,
#bp-presets-parent-container .bp-card-custom,
#ip-presets-parent-container .ip-card-custom,
.plp-pcard-custom { cursor: pointer; }

#fp-presets-parent-container .fp-card-custom .bws-card-create-btn,
#bp-presets-parent-container .bp-card-custom .bws-card-create-btn,
#ip-presets-parent-container .ip-card-custom .bws-card-create-btn,
.plp-pcard-custom .bws-card-create-btn {
    font-weight: 500; pointer-events: none;
    background: #d9b53c; border-color: #d9b53c;
    transition: background-color .2s ease, border-color .2s ease;
}
#fp-presets-parent-container .fp-card-custom .bws-card-cover--custom,
#bp-presets-parent-container .bp-card-custom .bws-card-cover--custom,
#ip-presets-parent-container .ip-card-custom .bws-card-cover--custom,
.plp-pcard-custom .bws-card-cover--custom {
    background: #d9b53c;
    transition: background-color .2s ease;
}
#fp-presets-parent-container .fp-card-custom:hover .bws-card-create-btn,
#bp-presets-parent-container .bp-card-custom:hover .bws-card-create-btn,
#ip-presets-parent-container .ip-card-custom:hover .bws-card-create-btn,
#fp-presets-parent-container .fp-card-custom:hover .bws-card-cover--custom,
#bp-presets-parent-container .bp-card-custom:hover .bws-card-cover--custom,
#ip-presets-parent-container .ip-card-custom:hover .bws-card-cover--custom,
.plp-pcard-custom:hover .bws-card-create-btn,
.plp-pcard-custom:hover .bws-card-cover--custom {
    background: var(--color-accent, #ffd84b); border-color: var(--color-accent, #ffd84b);
}

/* Shared modal action row: Load (fills) + square trash-icon delete button beside
   it. Used by the packs + breathwork program info modals. */
/* Pack modal: square module toggles above Load Pack. One per module in the pack,
   on by default; a toggled-off module is left out of the load. The icon inside is
   a clone of that module's Module Control Bar icon, so it is sized from here. */
.prb-modsel:empty { display: none; }
.prb-modsel-label { font-size: 10px; font-weight: 700; letter-spacing: 0.06em; text-transform: uppercase; color: var(--text-muted, rgba(255,255,255,0.45)); margin-bottom: 7px; }
.prb-modsel-row { display: flex; flex-wrap: wrap; gap: 7px; margin-bottom: 14px; }
.prb-modsel-btn {
    width: 34px; height: 34px; padding: 0; border-radius: 7px; cursor: pointer;
    display: flex; align-items: center; justify-content: center;
    background: var(--bg-dark-heavy, rgba(0,0,0,0.25));
    border: 1px solid var(--color-border, rgba(255,255,255,0.08));
    transition: opacity 0.15s, border-color 0.15s, background 0.15s;
}
.prb-modsel-btn svg { width: 19px; height: 19px; display: block; }
.prb-modsel-code { font-size: 10px; font-weight: 700; color: var(--color-accent, #ffd84b); }
.prb-modsel-btn.is-on { border-color: rgba(255,216,75,0.45); background: rgba(255,216,75,0.10); }
/* Off reads as "excluded from this load", not as disabled — it stays clickable. */
.prb-modsel-btn:not(.is-on) { opacity: 0.32; }
.prb-modsel-btn:hover { border-color: var(--color-accent, #ffd84b); }
/* "✓ Pack loaded" sits higher above "Close and Play" than the shared
   .fp-modal-loaded spacing puts it — the confirmation reads as its own line
   rather than as a caption stuck to the button. Pack modal only; the shared rule
   still serves the frequency/breathwork/bowls modals unchanged. */
.prb-modal-loaded { padding-bottom: 20px; }
/* Blank slot standing in for a button while the pack content is still in flight.
   Same 34px box (border-box, so it matches the button's border-box UA default),
   drawn as nothing — it holds the row's height and wrap so the Load Pack button
   below it never moves once the real icons arrive. */
   .prb-modsel-slot { box-sizing: border-box; width: 34px; height: 34px; display: block; }
/* Icons pop in left to right, 50ms apart (the delay is set per index in JS).
   `backwards` fill so a button waiting for its turn is already invisible, while
   the finished state comes from the rules above — not from the keyframes, which
   would otherwise pin opacity at 1 and defeat the toggled-off dim. */
@keyframes prb-modsel-pop {
    from { opacity: 0; transform: scale(0.5); }
    60% { opacity: 1; transform: scale(1.08); }
    to { opacity: 1; transform: scale(1); }
}
.prb-modsel-pop { animation: prb-modsel-pop 260ms cubic-bezier(0.22, 1, 0.36, 1) backwards; }
@media (prefers-reduced-motion: reduce) {
    .prb-modsel-pop { animation: none; }
}

.bws-modal-actionrow { display: flex; gap: 8px; align-items: stretch; }
.bws-modal-actionrow > button:not(.bws-modal-sq):not(.bws-modal-del-sq) { width: auto; flex: 1; }
/* The square icon buttons that sit on the end of a modal's action row. One box,
   two hovers: .bws-modal-sq fills accent like every other icon button, and the
   delete variant goes red because it destroys something. */
.bws-modal-sq,
.bws-modal-del-sq {
    flex: 0 0 auto; width: 44px; padding: 0; border-radius: 8px; cursor: pointer;
    display: flex; align-items: center; justify-content: center;
    border: 1px solid var(--border-accent-light, rgba(255,216,75,0.25));
    background: var(--bg-dark-lighter, #1a1a1a); color: var(--color-accent, #ffd84b);
    transition: background .15s ease, border-color .15s ease, color .15s ease;
}
.bws-modal-sq:hover:not([disabled]) { background: var(--color-accent, #ffd84b); border-color: var(--color-accent, #ffd84b); color: #000; }
.bws-modal-sq[disabled] { opacity: 0.55; cursor: default; }
.bws-modal-del-sq:hover { background: #f44336; border-color: #f44336; color: #fff; }
.bws-modal-sq svg,
.bws-modal-del-sq svg { display: block; pointer-events: none; }

/* Edit (✎) on a pack's info modal — mirrors the ✕ close button across the cover:
   same size, same offset, same circle, on the other corner. Filled accent rather
   than the close button's translucent black, because this one is an action on the
   pack, not a way out of the dialog. */
.bws-modal-edit-btn {
    position: absolute; top: 10px; left: 10px; z-index: 3;
    width: 28px; height: 28px; padding: 0; border-radius: 50%; cursor: pointer;
    display: flex; align-items: center; justify-content: center;
    background: #d9b53c; color: #000;
    border: 1px solid #d9b53c;
    box-shadow: 0 2px 8px rgba(0,0,0,0.45);
    transition: background-color .2s ease, border-color .2s ease, transform .15s ease;
}
/* The same darker-yellow → accent-yellow move the Load button beside it makes,
   rather than a lift to white: two accent buttons in one modal should light up
   the same way. */
.bws-modal-edit-btn:hover {
    background: var(--color-accent, #ffd84b); border-color: var(--color-accent, #ffd84b);
    transform: scale(1.08);
}
.bws-modal-edit-btn svg { display: block; pointer-events: none; }
/* Same badge over a card's cover rather than a modal's — the AI track/meditation
   cards and the singing-bowls session presets, which have no info modal to put it
   in. The card is smaller than a modal, so the button is too. (A saved-playlist
   card has no ✎: its edit lives on the info modal its ⓘ opens.) */
.plp-pcard-edit { top: 8px; left: 8px; width: 26px; height: 26px; }

/* "Replace the contents" — the one destructive control in an edit dialog, so it
   is boxed off from the text fields above it rather than sitting in the flow as
   another field.

   Unscoped on purpose. Every edit dialog in the app shares this block — the pack
   builder and the three timer modules through .bws-cover-dialog, the handpan,
   drum and gong through their own . {h,d,g}cp-save-dialog, which style.css knows
   nothing about. The class names are unique enough to stand on their own, and
   the checkbox reset below still outweighs each dialog's blanket `input` rule on
   specificity alone. */
.prb-edit-replace {
    margin: 0 0 12px; padding: 9px 10px; border-radius: 6px;
    background: var(--bg-dark-lighter, #1a1a1a);
    border: 1px solid var(--border-accent-light, rgba(255,216,75,0.25));
}
/* Written as a descendant of .prb-edit-replace on purpose. Every one of these
   dialogs carries a `.<x>-save-dialog label { display: block }` rule at (0,1,1);
   a bare `.prb-edit-replace-toggle` is (0,1,0) and loses to it, which collapses
   this row back to an inline checkbox jammed against its text. The extra class
   takes it to (0,2,0) and it wins wherever the dialog's own stylesheet sits in
   the cascade. */
.prb-edit-replace .prb-edit-replace-toggle {
    display: flex; align-items: center; gap: 8px; cursor: pointer; margin: 0;
    font-size: 13px; font-weight: 600; color: var(--text-primary, #fff);
}
/* Every one of these dialogs styles `input` as a full-width text field (padding,
   margin, border, background) — on a checkbox that renders as a full-width block
   and throws the whole box out. Reset it back to a native checkbox: this outweighs
   each dialog's (0,1,1) rule on specificity, so it wins even where the module
   injects its stylesheet after this one. The 15px width pairs with the 8px flex
   gap above to put the label 23px in — which is where .prb-edit-replace-note's
   left margin lines the caption up underneath it. */
.prb-edit-replace .prb-edit-replace-toggle input[type="checkbox"] {
    flex: 0 0 auto; width: 15px; height: 15px; min-width: 0;
    padding: 0; margin: 0; border: 0; border-radius: 0; background: none;
    -webkit-appearance: checkbox; appearance: checkbox;
    accent-color: var(--color-accent, #ffd84b);
}
.prb-edit-replace .prb-edit-replace-note {
    margin: 5px 0 0 23px; font-size: 11px; line-height: 1.4; color: var(--text-muted, rgba(255,255,255,0.6));
}
/* The note reads as a plain caption while the toggle is off and the save is
   harmless; ticked on it is announcing an overwrite, so it takes the accent. */
.prb-edit-replace .prb-edit-replace-note.is-on { color: var(--color-accent, #ffd84b); }
.prb-edit-replace .prb-edit-replace-modules { margin-top: 10px; }
.prb-edit-replace .prb-edit-replace-modules > label { display: block; margin-bottom: 6px; }
.prb-edit-replace .prb-edit-replace-err:not(:empty) {
    margin-top: 8px; font-size: 12px; line-height: 1.45; color: #ff9f43;
}

/* Admin-only: promote this personal preset/pack/program to the Brainwavesounds
   catalog. Sits above the Load row as a full-width outlined button — deliberately
   quieter than Load, which is still the action almost every visit wants. */
.bws-modal-publish {
    display: block; width: 100%; margin-bottom: 8px; padding: 10px 12px;
    border-radius: 8px; cursor: pointer; font-size: 13px; font-weight: 600;
    background: transparent; color: var(--color-accent, #ffd84b);
    border: 1px solid var(--border-accent-medium, rgba(255,216,75,0.4));
    transition: background .15s ease, color .15s ease, border-color .15s ease;
}
.bws-modal-publish:hover:not(:disabled) {
    background: var(--color-accent, #ffd84b); color: #111; border-color: var(--color-accent, #ffd84b);
}
.bws-modal-publish:disabled { opacity: 0.6; cursor: default; }

/* Shared card-sized square trash button — the delete control on personal saved
   cards (timers + playlists). Sits beside the flex:1 Load button and stretches
   to its height. Same look as the modal trash, sized down for cards. */
.bws-card-del {
    flex: 0 0 auto; width: 34px; padding: 0; border-radius: 5px; cursor: pointer;
    display: flex; align-items: center; justify-content: center;
    border: 1px solid var(--border-accent-light, rgba(255,216,75,0.3));
    background: var(--bg-dark-lighter, #1a1a1a); color: var(--color-accent, #ffd84b);
    transition: background .15s ease, border-color .15s ease, color .15s ease;
}
.bws-card-del:hover { background: #f44336; border-color: #f44336; color: #fff; }
.bws-card-del svg { display: block; pointer-events: none; width: 16px; height: 16px; }

/* ── Shared save-dialog cover art + "generating…" splash ──────────────────────
   Scoped to .bws-cover-dialog, not to one module's dialog, so every save dialog
   that offers AI cover art and the generating splash (Pack Builder, Isochronic
   Presets Builder, …) shares this one block. Add the class alongside the
   dialog's own class (.fp-save-dialog / .ip-save-dialog) and keep the prb-*
   class names on the children. The dialogs are appended to <body>, so they
   cannot be scoped by a module container. */
.bws-cover-dialog .prb-cover-section { display: flex; flex-direction: column; gap: 6px; margin: 4px 0 12px; }
/* The label owns a full-width row above the two columns, so the textarea and
   the preview both start at the same y. */
.bws-cover-dialog .prb-cover-label { display: block; margin: 0; }
.bws-cover-dialog .prb-cover-sub { font-weight: 400; font-size: 11px; color: var(--text-muted, #999); }
.bws-cover-dialog .prb-cover-row { display: flex; gap: 12px; align-items: flex-start; }
.bws-cover-dialog .prb-cover-preview {
    flex: 0 0 110px; width: 110px; height: 110px; border-radius: 8px; overflow: hidden;
    display: flex; align-items: center; justify-content: center;
    background: var(--bg-dark-lighter, #1a1a1a);
    border: 1px solid var(--border-accent-light, rgba(255,216,75,0.25));
}
.bws-cover-dialog .prb-cover-preview img { width: 100%; height: 100%; object-fit: cover; display: block; }
.bws-cover-dialog .prb-cover-ph { font-size: 11px; color: var(--text-muted, #888); text-align: center; padding: 6px; }
/* align-self: stretch — only the LEFT column grows to the row's height. The
   right one keeps its natural height (preview + button) and stays pinned to
   the top, so a description field that grows past it pulls nothing down. */
.bws-cover-dialog .prb-cover-controls { flex: 1; align-self: stretch; display: flex; flex-direction: column; min-width: 0; }
.bws-cover-dialog .prb-cover-prompt {
    width: 100%; min-height: 66px; flex: 1 1 auto; resize: vertical; font-size: 12px; line-height: 1.4;
    padding: 8px; border-radius: 6px; box-sizing: border-box;
    background: var(--bg-dark-light, #141414); color: var(--text-primary, #fff);
    border: 1px solid var(--border-accent-light, rgba(255,216,75,0.25));
}
/* Buttons in this dialog. The base is the neutral outline one — .prb-action-btn
   is otherwise only defined inside .bws-ai-builder, so without this it fell
   through to the browser's own grey box. Generate/Replace cover overrides it
   below in yellow: same specificity, so that rule has to stay AFTER this one. */
.bws-cover-dialog .prb-action-btn {
    flex: 0 0 auto; padding: 8px 14px; border-radius: 6px; cursor: pointer;
    font-size: 12px; font-weight: 700; font-family: inherit;
    border: 1px solid var(--border-accent-light, rgba(255,216,75,0.25));
    background: var(--bg-dark-lighter, #1a1a1a); color: var(--text-muted, #aaa);
    transition: border-color .2s ease, color .2s ease;
}
.bws-cover-dialog .prb-action-btn:hover:not(:disabled) {
    border-color: var(--color-accent, #ffd84b); color: var(--color-accent, #ffd84b);
}
.bws-cover-dialog .prb-action-btn:disabled { opacity: 0.6; cursor: default; }
/* The paid path, and the emphatic one: darker yellow at rest, accent under the
   cursor. The upload button beside it stays neutral on purpose. */
.bws-cover-dialog .prb-cover-gen {
    padding: 8px 14px; border-radius: 6px; cursor: pointer; font-size: 12px; font-weight: 700;
    border: 1px solid #d9b53c; background: #d9b53c; color: #000;
    transition: background-color .2s ease, border-color .2s ease;
}
.bws-cover-dialog .prb-cover-gen:hover:not(:disabled) {
    background: var(--color-accent, #ffd84b); border-color: var(--color-accent, #ffd84b);
    /* Restated, not inherited: the neutral base above lifts its text to accent on
       hover, which on this button would be yellow on yellow. Black is the only
       thing that reads on the accent fill, at rest and under the cursor alike. */
    color: #000;
}
.bws-cover-dialog .prb-cover-gen:disabled { opacity: 0.6; cursor: default; }
.bws-cover-dialog .prb-cover-status { font-size: 12px; color: var(--color-accent, #ffd84b); line-height: 1.4; text-align: center; margin-bottom: 16px; }
/* Close (✕) sits above the generating splash so it stays clickable while loading. */
.bws-cover-dialog .fp-modal-close { z-index: 4; }

/* Presets-Builder save dialog: give it room so the full preset description and
   image-prompt text are visible without manual resizing, and let it scroll if the
   generated cover + fields exceed the viewport. Scoped to .bws-cover-dialog so the
   other modules' narrow save dialogs are unaffected. position:relative anchors the
   "generating…" splash overlay. */
/* The 100px is a 50px breathing gap above and below. The overlay centres the
   dialog inside its own 24px padding, so a cap of 100vh-100px leaves 52px of
   slack split evenly — 26px each side on top of that padding. A tall dialog
   (long description, cover preview, module picker) therefore stops short of the
   viewport edges and scrolls inside itself instead of running into them. */
.bws-cover-dialog { position: relative; max-width: 640px; max-height: calc(100vh - 100px); overflow-y: auto; }
.bws-cover-dialog #prb-cs-desc,
.bws-cover-dialog #ip-save-desc { min-height: 84px; }
.bws-cover-dialog .prb-cover-prompt { min-height: 112px; }

/* Cover section: description on the LEFT, 16:9 preview on the RIGHT with the
   Generate button below it.

   The two line up at rest without either side being anchored to the other. The
   row is as tall as its tallest column, and only the left column stretches to
   it: when the description is short the row is the preview+button's height and
   the textarea grows down to meet the button; when the description outgrows
   them the row follows the textarea and the right column simply stays at the
   top, button still tucked under the preview where it belongs. Which is why the
   button is NOT pushed down with `margin-top: auto` — that would tie it to the
   textarea's bottom edge and walk it down the dialog as the field grew. */
.bws-cover-dialog .prb-cover-right { flex: 0 0 220px; display: flex; flex-direction: column; gap: 8px; }
.bws-cover-dialog .prb-cover-preview { width: 100%; aspect-ratio: 16 / 9; height: auto; flex: 0 0 auto; }
/* Square variant. A recording's cover is generated 1:1 so it sits beside the
   AI-song artwork on the My Library cards without being cropped to fit, and the
   preview has to show the shape that will actually be made. */
.bws-cover-dialog .prb-cover-preview--square { aspect-ratio: 1 / 1; }
.bws-cover-dialog .prb-cover-right .prb-cover-gen { align-self: stretch; text-align: center; }

/* The Auto toggle beside the Duration slider in the AI Meditation Creator: the
   parameter is optional upstream, so "Auto" is the absence of it, not a value. */
.gmb-dialog .gm-ai-autorow {
    display: flex; align-items: center; gap: 7px; margin: 2px 0 8px;
    font-size: 12px; color: var(--text-muted, #888); cursor: pointer;
}
.gmb-dialog .gm-ai-autorow input { cursor: pointer; }
.gmb-dialog .gm-ai-autorow:has(input:disabled) { opacity: .45; cursor: default; }
.gmb-dialog #gm-ai-model { width: 100%; box-sizing: border-box; }

.bws-cover-dialog .medm-ed-uploadrow,
.bws-cover-dialog .gm-ed-uploadrow,
.bws-cover-dialog .bws-ed-uploadrow {
    display: flex; flex-wrap: wrap; align-items: center; gap: 10px; margin-top: 10px;
}
/* The fine print rides on the row, to the right of the Upload button, rather
   than under it — small and dimmed, so it explains without competing. */
.bws-cover-dialog .medm-ed-uploadrow .bws-group-description,
.bws-cover-dialog .gm-ed-uploadrow .bws-group-description,
.bws-cover-dialog .bws-ed-uploadrow .bws-group-description {
    flex: 1 1 180px; min-width: 0; margin: 0;
    font-size: 10px; line-height: 1.35; opacity: 0.8;
}
/* The picked file's name takes the whole next line, and takes none at all until
   there is a name to show. */
.bws-cover-dialog .medm-ed-filename,
.bws-cover-dialog .gm-ed-filename,
.bws-cover-dialog .bws-ed-filename {
    flex: 1 1 100%; min-width: 0; font-size: 12px; color: var(--text-muted, #888);
    overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.bws-cover-dialog .medm-ed-filename:empty,
.bws-cover-dialog .gm-ed-filename:empty,
.bws-cover-dialog .bws-ed-filename:empty { display: none; }
/* Spinner shown in the Generate button while the image is being created. */
/* Drawn in black, not accent: this spins INSIDE the yellow Generate button, so a
   yellow ring on a yellow field would be invisible. */
.bws-cover-dialog .prb-cover-spinner {
    display: inline-block; width: 12px; height: 12px; margin-right: 8px; vertical-align: -1px;
    border: 2px solid rgba(0,0,0,0.28); border-top-color: #000;
    border-radius: 50%; animation: prb-cover-spin 0.7s linear infinite;
}
@keyframes prb-cover-spin { to { transform: rotate(360deg); } }
/* Build/save reporting inside the save dialog — the module's own status line sits
   behind this overlay, so anything the user has to act on is repeated here. */
.prb-cs-notice {
    margin: 0 0 14px; padding: 10px 12px; border-radius: 6px;
    font-size: 12.5px; line-height: 1.5;
    background: var(--bg-dark-lighter, rgba(255,255,255,0.02));
    border: 1px solid var(--border-accent-light, rgba(255,216,75,0.25));
    color: var(--text-muted, #aaa);
}
.prb-cs-notice.is-info { color: var(--color-accent, #ffd84b); }
.prb-cs-notice.is-warn { color: #ff9f43; border-color: rgba(255,159,67,0.5); }

.bws-cover-dialog .prb-cover-status.warn { color: #ff9f43; }
.bws-cover-dialog .prb-cover-status.info { color: var(--color-accent, #ffd84b); }

/* "Your preset is being generated" splash — animated logo over the empty form,
   fades out (revealSaveDialog) once every field is populated. The splash is the
   dialog's whole visible surface, so it stays clear and lets the panel's glass
   show; the form beneath is hidden outright rather than covered, or it would
   read through as clutter. The close button is excluded — it sits above the
   splash and stays usable while the build runs. */
.bws-cover-dialog.is-loading { min-height: 340px; }
/* The generating splash is shared by the save dialogs (.bws-cover-dialog) and by
   the AI builder modals (.bws-build-dialog), which show it in place while a build
   is running. Re-scoped rather than duplicated — same rule as the rest of the
   prb-* design system. */
.bws-cover-dialog .prb-cs-loading,
.bws-build-dialog .prb-cs-loading {
    position: absolute; inset: 0; z-index: 3; border-radius: 6px;
    display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 18px; padding: 24px;
    background: transparent;
    transition: opacity 0.4s ease;
}
.bws-cover-dialog .prb-cs-loading.prb-cs-loading-hide,
.bws-build-dialog .prb-cs-loading.prb-cs-loading-hide { opacity: 0; pointer-events: none; }
.bws-cover-dialog.is-loading > *:not(.prb-cs-loading):not(.fp-modal-close),
.bws-build-dialog.is-loading > *:not(.prb-cs-loading):not(.bws-build-close) {
    visibility: hidden;
}
.bws-cover-dialog .prb-cs-loading-video,
.bws-build-dialog .prb-cs-loading-video { width: 160px; height: 160px; object-fit: contain; }
.bws-cover-dialog .prb-cs-loading-text,
.bws-build-dialog .prb-cs-loading-text { margin: 0; color: var(--color-accent, #ffd84b); font-size: 14px; text-align: center; }
/* The splash needs a minimum canvas: an empty build dialog can be shorter than
   the logo video, which would crop it. */
.bws-build-dialog.is-loading { min-height: 320px; }

/* ── Shared AI preset builder ─────────────────────────────────────────────────
   Scoped to .bws-ai-builder, not to one module, so every module that grows an
   "AI presets builder" section (Pack Builder, Isochronic Presets Builder, …)
   gets the identical layout from this one block. Add the class to the section's
   root element; keep the prb-* class names on the children. */
.bws-ai-builder .prb-describe-label {
    display: block; font-size: 13px; font-weight: 700; letter-spacing: 0.03em;
    color: var(--color-accent, #ffd84b); margin-bottom: 8px;
}
.bws-ai-builder .prb-describe {
    width: 100%; box-sizing: border-box; min-height: 84px; resize: vertical;
    padding: 10px 12px; border-radius: 8px; font-size: 13px; font-family: inherit; line-height: 1.5;
    border: 1px solid var(--border-accent-light, rgba(255,216,75,0.25));
    background: var(--bg-dark-light, #141414); color: var(--text-primary, #fff);
}
.bws-ai-builder .prb-describe:focus { outline: none; border-color: var(--color-accent, #ffd84b); }
/* Transient hint under the module list (e.g. "tick a module first"). */
.bws-ai-builder .prb-hint {
    display: none; margin-top: 10px; font-size: 12px; line-height: 1.4; color: #ff9f43;
}
.bws-ai-builder .prb-hint.show { display: block; }

/* Modules header: title on the left, Select-active / Save buttons on the right. */
.bws-ai-builder .prb-modules-header {
    display: flex; align-items: center; justify-content: space-between; flex-wrap: wrap;
    gap: 12px; margin: 16px 0 8px;
}
/* Title matches the "Describe the preset…" label style. */
.bws-ai-builder .prb-modules-label {
    display: block; font-size: 13px; font-weight: 700; letter-spacing: 0.03em;
    color: var(--color-accent, #ffd84b);
}
/* Standing caption under that title, explaining that the selection is a starting
   point rather than a limit. Muted and un-bold so it reads as a note on the
   label above it, not as a second label — and distinct from .prb-hint, which is
   the transient orange warning that flashes and auto-hides. */
.bws-ai-builder .prb-modules-note {
    margin: -2px 0 8px; font-size: 12px; line-height: 1.45;
    color: var(--text-muted, rgba(255,255,255,0.6));
}
/* Two columns of "icon + name" toggles. The icon box is the same control the
   pack info modal uses for "Modules to load" — .prb-modsel-btn carries the look —
   but here the whole row is the button, so the lit/dimmed state is driven off
   `is-on` on the row rather than on the icon itself. */
/* The .bws-cover-dialog half of each selector below is the SAME picker inside the
   save/edit dialog ("Modules to capture", when replacing a pack's contents). That
   dialog drops .bws-ai-builder and styles its children through .bws-cover-dialog,
   so the scope is listed rather than the declarations duplicated. */
   .bws-ai-builder .prb-modules-list,
   .bws-cover-dialog .prb-modules-list {
    display: grid; grid-template-columns: repeat(2, 1fr); gap: 6px 14px;
    padding: 12px; border-radius: 8px;
    background: var(--bg-dark-lighter, #1a1a1a); border: 1px solid var(--border-accent-light, rgba(255,216,75,0.25));
}
@media (max-width: 700px) {
    .bws-ai-builder .prb-modules-list,
    .bws-cover-dialog .prb-modules-list { grid-template-columns: 1fr; }
}
.bws-ai-builder .prb-module-row,
.bws-cover-dialog .prb-module-row {
    display: flex; align-items: center; gap: 10px; padding: 4px 6px; border-radius: 6px; cursor: pointer;
    background: none; border: 0; text-align: left; font: inherit;
    color: var(--text-primary, #fff); font-size: 13px;
}
.bws-ai-builder .prb-module-row:hover,
.bws-cover-dialog .prb-module-row:hover { background: rgba(255,216,75,0.07); }
/* opacity:1 undoes the shared `.prb-modsel-btn:not(.is-on)` dim — here the row,
   not the icon, carries `is-on`. */
.bws-ai-builder .prb-module-icon,
.bws-cover-dialog .prb-module-icon { flex: 0 0 auto; opacity: 1; }
.bws-ai-builder .prb-module-name,
.bws-cover-dialog .prb-module-name { flex: 1; min-width: 0; }
/* Off reads as "not in this pack", not as disabled — the row stays clickable. */
.bws-ai-builder .prb-module-row:not(.is-on) .prb-module-icon,
.bws-ai-builder .prb-module-row:not(.is-on) .prb-module-name,
.bws-cover-dialog .prb-module-row:not(.is-on) .prb-module-icon,
.bws-cover-dialog .prb-module-row:not(.is-on) .prb-module-name { opacity: 0.32; }
.bws-ai-builder .prb-module-row.is-on .prb-module-icon,
.bws-cover-dialog .prb-module-row.is-on .prb-module-icon {
    border-color: rgba(255,216,75,0.45); background: rgba(255,216,75,0.10);
}
.bws-ai-builder .prb-module-row:hover .prb-module-icon,
.bws-cover-dialog .prb-module-row:hover .prb-module-icon { border-color: var(--color-accent, #ffd84b); }
.bws-ai-builder .prb-builder-actions { display: flex; flex-wrap: wrap; gap: 10px; margin: 0; }
.bws-ai-builder .prb-action-btn {
    padding: 10px 16px; border-radius: 6px; cursor: pointer; font-size: 13px; font-weight: 700;
    border: 1px solid var(--border-accent-light, rgba(255,216,75,0.25));
    background: var(--bg-dark-lighter, #1a1a1a); color: var(--text-muted, #aaa);
}
.bws-ai-builder .prb-action-btn:hover { border-color: var(--color-accent, #ffd84b); color: var(--color-accent, #ffd84b); }
/* Primary action: darker yellow at rest, accent yellow under the cursor — the
   same two-tone as .fp-modal-load and the save dialogs' Save. */
.bws-ai-builder .prb-save-btn {
    border-color: #d9b53c; background: #d9b53c; color: #000;
    transition: background-color .2s ease, border-color .2s ease;
}
.bws-ai-builder .prb-save-btn:hover:not(:disabled) {
    background: var(--color-accent, #ffd84b); border-color: var(--color-accent, #ffd84b); color: #000;
}
.bws-ai-builder .prb-status {
    margin-top: 12px; padding: 10px 12px; border-radius: 6px; font-size: 13px; line-height: 1.5;
    background: var(--bg-dark-lighter, #1a1a1a); border: 1px solid var(--border-accent-light, rgba(255,216,75,0.25));
    color: var(--text-muted, #aaa);
}
.bws-ai-builder .prb-status-info { color: var(--color-accent, #ffd84b); }
.bws-ai-builder .prb-status-warn { color: #ff9f43; border-color: rgba(255,159,67,0.5); }
.bws-ai-builder .prb-spinner {
    display: inline-block; width: 13px; height: 13px; margin-right: 8px; vertical-align: -2px;
    border: 2px solid var(--border-accent-light, rgba(255,216,75,0.25));
    border-top-color: var(--color-accent, #ffd84b); border-radius: 50%;
    animation: prb-spin 0.7s linear infinite;
}
@keyframes prb-spin { to { transform: rotate(360deg); } }

/* ── Model, effort and price per module (admin) ──────────────────────────────
   Model, two effort columns (inside a pack and built alone), the resulting price
   and the margin against what that combination has really cost. Kept to a plain
   table because the whole value of the panel is being able to read a column top
   to bottom and see where it differs from the defaults. The price cube lives in
   a drawer per module rather than on screen at once: fourteen modules by four
   engines by six efforts is a wall nobody reads. */
.bws-eff-table {
    width: 100%; border-collapse: collapse; margin: 12px 0 4px;
    font-size: 12.5px;
}
.bws-eff-table th {
    text-align: left; padding: 6px 8px; font-size: 11px; letter-spacing: 0.05em;
    text-transform: uppercase; color: var(--text-muted, #888);
    border-bottom: 1px solid var(--color-border, rgba(255,255,255,0.12));
}
.bws-eff-table td {
    padding: 5px 8px; border-bottom: 1px solid var(--bg-dark-light, rgba(255,255,255,0.05));
    color: var(--text-bright, rgba(255,255,255,0.8)); vertical-align: middle;
}
.bws-eff-table td:first-child { width: 26%; }
/* The selects were sized for a three-column table. With model, two efforts,
   price, margin and the row actions there are seven, so they give up the
   width the new columns need rather than forcing the panel wider still. */
.bws-eff-table .bws-input { width: 100%; min-width: 92px; }
/* The panel is now as wide as the window allows, so the table has the room it
   needs and the sideways scroll is a fallback for a genuinely small screen,
   not the normal case. min-width is what triggers it, so it is set to the
   point below which the columns really stop fitting rather than to the width
   the table would like. */
.bws-usage-panel--xwide .bws-usage-body { overflow-x: auto; }
.bws-eff-table { min-width: 880px; }
/* The two numbers the panel exists to produce. Tabular figures so a column of
   them lines up and an outlier is visible without reading each one. */
.bws-eff-table .bws-eff-live,
.bws-eff-table .bws-eff-loss { font-variant-numeric: tabular-nums; white-space: nowrap; }
.bws-eff-table .bws-eff-loss { color: var(--color-danger, #f44336); font-weight: 700; }
.bws-eff-runs { color: var(--text-muted, #888); font-size: 10.5px; }
/* Row actions. Small and quiet: they are per-row tools, not the panel's Save. */
.bws-eff-prices-btn, .bws-eff-test {
    padding: 3px 8px; border-radius: 4px; cursor: pointer;
    font-size: 11px; font-family: inherit;
    color: var(--color-text, #fff);
    background: transparent;
    border: 1px solid var(--border-accent-light, rgba(255,216,75,0.25));
}
.bws-eff-prices-btn:hover:not(:disabled),
.bws-eff-test:hover:not(:disabled) { background: rgba(255,216,75,0.12); }
.bws-eff-test:disabled { opacity: 0.5; cursor: default; }

/* The price cube for one module: engines down the side, efforts across. */
.bws-eff-drawer > td { background: var(--bg-dark-light, rgba(255,255,255,0.04)); }
.bws-eff-prices { width: 100%; border-collapse: collapse; font-size: 11.5px; }
.bws-eff-prices th {
    text-align: left; padding: 4px 6px; font-size: 10px; letter-spacing: 0.05em;
    text-transform: uppercase; color: var(--text-muted, #888); border: 0;
}
.bws-eff-prices td { padding: 3px 6px; border: 0; }
.bws-eff-prices .bws-input {
    width: 68px; min-width: 0; padding: 4px 6px;
    font-variant-numeric: tabular-nums; text-align: right;
}
/* Measured cost under its own box: the number the price has to clear, where the
   price is typed rather than in a legend somewhere else. */
.bws-eff-cost { display: block; margin-top: 2px; font-size: 10px; color: var(--text-muted, #888); }
.bws-eff-note { margin-top: 8px; font-size: 11px; color: var(--text-muted, #888); }

/* The second half of the panel: the fixed-price actions that are not module
   builds. Its own table under a rule rather than its own modal — these are
   the same decision as the prices above, taken about different work. */
.bws-eff-rule {
    margin: 22px 0 14px; border: 0;
    border-top: 1px solid var(--color-border, rgba(255,255,255,0.12));
}
.bws-eff-subhead {
    margin: 0 0 6px; font-size: 13px; font-weight: 700;
    color: var(--color-text, #fff);
}
/* Two columns, so it does not inherit the seven-column table's min-width and
   drag a scrollbar under a table that fits comfortably. */
.bws-act-table { min-width: 0; }
.bws-act-table td:first-child { width: 46%; }
.bws-act-table .bws-input { width: 88px; min-width: 0; text-align: right;
    font-variant-numeric: tabular-nums; }
.bws-act-table .bws-eff-note { margin-top: 2px; }
/* A per-model action's alternatives sit on one row: they are versions of the
   same action, not separate ones. */
.bws-act-models { display: flex; flex-wrap: wrap; gap: 10px 16px; }
.bws-act-model { display: flex; flex-direction: column; gap: 3px; font-size: 10.5px;
    color: var(--text-muted, #888); }
/* The composed modules — handpan, drum, gong. They are the ones where effort
   changes the music rather than a handful of settings, and the slowest to build,
   so they are the rows worth finding first. */
.bws-eff-table tr.bws-eff-slow td:first-child { font-weight: 600; }
.bws-eff-tag {
    display: inline-block; margin-left: 6px; padding: 1px 6px; border-radius: 3px;
    font-size: 9.5px; letter-spacing: 0.06em; text-transform: uppercase;
    color: var(--text-muted, #888);
    background: var(--bg-dark-medium, rgba(255,255,255,0.06));
}

/* ── Pack plan review ────────────────────────────────────────────────────────
   The approval screen between planning a pack and rendering it. A pack is now
   built in two stages and only the second one costs real credits, so the plan is
   shown first and the user confirms it. Styled off the builder dialog it is
   rendered into (.bws-build-dialog), so the modal never changes shape between
   the picker, this and the save form. */
.bws-ai-builder .prb-plan-desc {
    margin: 0 0 14px; font-size: 13px; line-height: 1.5;
    color: var(--text-bright, rgba(255,255,255,0.8));
}
.bws-ai-builder .prb-plan-facts {
    display: flex; flex-wrap: wrap; gap: 8px; margin-bottom: 4px;
}
.bws-ai-builder .prb-plan-fact {
    flex: 1 1 auto; min-width: 120px; padding: 8px 10px; border-radius: 6px;
    background: var(--bg-dark-light, rgba(0,0,0,0.03));
    border: 1px solid var(--border-accent-medium, rgba(255,216,75,0.18));
    font-size: 12px; color: var(--text-bright, rgba(255,255,255,0.8)); line-height: 1.4;
}
.bws-ai-builder .prb-plan-fact span {
    display: block; margin-bottom: 2px; font-size: 10px; letter-spacing: 0.06em;
    text-transform: uppercase; color: var(--text-muted, #888);
}
.bws-ai-builder .prb-plan-layers { display: flex; flex-direction: column; gap: 6px; }
.bws-ai-builder .prb-plan-layer {
    padding: 9px 11px; border-radius: 6px;
    background: var(--bg-dark-light, rgba(0,0,0,0.03));
    border: 1px solid var(--color-border, rgba(255,255,255,0.12));
    border-left-width: 3px;
}
/* The layer the pack is built around: losing it fails the whole build, so it is
   marked here the same way the server treats it. */
.bws-ai-builder .prb-plan-layer.is-primary {
    border-left-color: var(--color-accent, #ffd84b);
}
.bws-ai-builder .prb-plan-layer-name {
    font-size: 12.5px; font-weight: 600; color: var(--text-white, #fff);
}
.bws-ai-builder .prb-plan-layer-name em {
    font-style: normal; font-weight: 400; color: var(--text-muted, #888);
}
.bws-ai-builder .prb-plan-layer-role {
    margin-top: 3px; font-size: 11.5px; line-height: 1.45; color: var(--text-muted, #999);
}
/* Back / Build is a decision, not a pair of equals — Build carries the weight. */
.bws-ai-builder .prb-ai-actions #prb-plan-back { flex: 0 0 auto; }
.bws-ai-builder .prb-ai-actions #prb-plan-build { flex: 1 1 auto; }

/* ── AI pack builder ─────────────────────────────────────────────────────── */
.bws-ai-builder .prb-ai-actions {
    display: flex; flex-wrap: wrap; align-items: center; gap: 10px; margin-top: 12px;
}
/* Quick build / Assisted build are the builder's primary pair, so they take the
   same two-tone rather than the dim accent-on-dark gradient they used to carry —
   which read as secondary next to a filled Save. */
.bws-ai-builder .prb-ai-btn {
    display: inline-flex; align-items: center; gap: 8px;
    border-color: #d9b53c; background: #d9b53c; color: #000;
    transition: background-color .2s ease, border-color .2s ease;
}
/* Stroke-style button icons follow the button's text color, which is black in
   both states now. */
.bws-ai-builder .prb-btn-icon { flex: 0 0 auto; display: block; }
.bws-ai-builder .prb-ai-btn:hover:not(:disabled) {
    background: var(--color-accent, #ffd84b); border-color: var(--color-accent, #ffd84b); color: #000;
}
/* Dim the yellow rather than swapping to a dark fill: the label is black now,
   and black on #1a1a1a is unreadable. */
.bws-ai-builder .prb-ai-btn:disabled { opacity: 0.45; cursor: default; }
.bws-ai-builder .prb-ai-hint { flex: 1; min-width: 180px; font-size: 11px; color: var(--text-muted, #888); line-height: 1.4; }

/* The AI builder dialogs that offer the Quick / Assisted pair — the pack builder
   the three timer modules and the four instrument builders. The pair is one
   choice between two equals, so the buttons split the dialog in half rather than
   sizing to their labels. Listed by id rather than styling .prb-ai-actions
   itself, because the same class also carries single-button rows (Save as new
   pack). */
#prb-ai-actions, #fb-ai-actions, #bb-ai-actions, #ib-ai-actions,
#hb-ai-actions, #db-ai-actions, #gb-ai-actions, #sbb-ai-actions {
    display: grid; grid-template-columns: 1fr 1fr; gap: 10px; flex-wrap: nowrap;
}
#prb-ai-actions .prb-ai-btn, #fb-ai-actions .prb-ai-btn,
#bb-ai-actions .prb-ai-btn, #ib-ai-actions .prb-ai-btn,
#hb-ai-actions .prb-ai-btn, #db-ai-actions .prb-ai-btn, #gb-ai-actions .prb-ai-btn,
#sbb-ai-actions .prb-ai-btn {
    width: 100%; justify-content: center; text-align: center;
}
/* Narrow dialogs can't fit two labelled buttons side by side. */
@media (max-width: 560px) {
    #prb-ai-actions, #fb-ai-actions, #bb-ai-actions, #ib-ai-actions,
    #hb-ai-actions, #db-ai-actions, #gb-ai-actions, #sbb-ai-actions { grid-template-columns: 1fr; }
}

.bws-ai-builder .prb-chat {
    margin-top: 14px; padding: 12px; border-radius: 10px;
    background: var(--bg-dark-lighter, #1a1a1a);
    border: 1px solid var(--border-accent-light, rgba(255,216,75,0.25));
}
.bws-ai-builder .prb-chat-log {
    display: flex; flex-direction: column; gap: 10px;
    max-height: 340px; overflow-y: auto; padding: 4px 4px 10px;
}
.bws-ai-builder .prb-chat-msg {
    max-width: 85%; padding: 9px 12px; border-radius: 12px; font-size: 13px; line-height: 1.5;
    white-space: pre-wrap; word-break: break-word;
}
.bws-ai-builder .prb-chat-user {
    align-self: flex-end; background: var(--color-accent, #ffd84b); color: #000; border-bottom-right-radius: 4px;
}
.bws-ai-builder .prb-chat-assistant {
    align-self: flex-start; background: var(--bg-dark-light, #141414); color: var(--text-primary, #fff);
    border: 1px solid var(--border-accent-light, rgba(255,216,75,0.2)); border-bottom-left-radius: 4px;
}
.bws-ai-builder .prb-chat-assistant.prb-chat-pending { color: var(--text-muted, #aaa); padding-top: 12px; padding-bottom: 12px; }
.bws-ai-builder .prb-typing { display: inline-flex; align-items: center; gap: 4px; }
.bws-ai-builder .prb-typing > span {
    width: 6px; height: 6px; border-radius: 50%;
    background: var(--color-accent, #ffd84b);
    opacity: 0.35; animation: prb-typing-bounce 1.2s infinite ease-in-out both;
}
.bws-ai-builder .prb-typing > span:nth-child(2) { animation-delay: 0.18s; }
.bws-ai-builder .prb-typing > span:nth-child(3) { animation-delay: 0.36s; }
@keyframes prb-typing-bounce {
    0%, 70%, 100% { transform: translateY(0); opacity: 0.35; }
    35% { transform: translateY(-4px); opacity: 1; }
}
@media (prefers-reduced-motion: reduce) {
    .bws-ai-builder .prb-typing > span { animation-duration: 2s; }
}
.bws-ai-builder .prb-chat-inputrow { display: flex; gap: 8px; margin-top: 6px; }
.bws-ai-builder .prb-chat-input {
    flex: 1; box-sizing: border-box; min-height: 40px; max-height: 120px; resize: vertical;
    padding: 9px 11px; border-radius: 8px; font-size: 13px; font-family: inherit; line-height: 1.4;
    border: 1px solid var(--border-accent-light, rgba(255,216,75,0.25));
    background: var(--bg-dark-light, #141414); color: var(--text-primary, #fff);
}
.bws-ai-builder .prb-chat-input:focus { outline: none; border-color: var(--color-accent, #ffd84b); }
.bws-ai-builder .prb-chat-send {
    flex: 0 0 auto; align-self: stretch; padding: 0 16px; border-radius: 8px; cursor: pointer;
    font-size: 13px; font-weight: 700;
    border: 1px solid var(--color-accent, #ffd84b); background: var(--color-accent, #ffd84b); color: #000;
}
.bws-ai-builder .prb-chat-send:hover { background: #fff; }
.bws-ai-builder .prb-chat-send:disabled { opacity: 0.5; cursor: default; }
.bws-ai-builder .prb-chat-actions { display: flex; flex-wrap: wrap; gap: 10px; margin-top: 10px; }
.bws-ai-builder .prb-chat-buildnow {
    border-color: var(--color-accent, #ffd84b); background: var(--color-accent, #ffd84b); color: #000;
}
.bws-ai-builder .prb-chat-buildnow:hover { background: #fff; }
.bws-ai-builder .prb-chat-buildnow:disabled { opacity: 0.55; cursor: default; }
.bws-ai-builder .prb-chat-close {
    padding: 10px 16px; border-radius: 6px; cursor: pointer; font-size: 13px; font-weight: 700;
    border: 1px solid var(--border-accent-light, rgba(255,216,75,0.25));
    background: transparent; color: var(--text-muted, #aaa);
}
.bws-ai-builder .prb-chat-close:hover { border-color: #ff9f43; color: #ff9f43; }

/* ── Media library AI dialogs on the shared builder look ──────────────────────
   Enhance / Extend / Boost carry `.bws-ai-builder` too, so everything above
   already applies to the children they share with the pack builder (labels,
   action buttons, the .prb-ai-btn primary, the round close, the h3).
   What is left below is the library's OWN children — mode tabs, sliders, the
   results list — pulled onto the same tokens. Values are not restated here:
   each rule points at the builder rule it is matching, and the two things that
   ARE restated (the input and the grouped-block surface) are written as one
   selector list with their builder counterpart so they cannot drift. */

/* Free-text fields take .prb-describe's skin without its textarea height.
   .bbp-frequency-input is really a numeric frequency box — right-aligned, fixed
   34px tall, accent-yellow text forced with !important. Prose fields borrow it
   for the border/background only, so all three of those have to be undone here
   or a title types itself yellow and right-aligned. */
.mlib-enhance-panel .bbp-frequency-input {
    width: 100%; height: auto; box-sizing: border-box; text-align: left;
    padding: 10px 12px; border-radius: 8px; font-size: 13px; font-family: inherit; line-height: 1.5;
    border: 1px solid var(--border-accent-light, rgba(255,216,75,0.25));
    background: var(--bg-dark-light, #141414);
    color: var(--text-primary, #fff) !important;
    box-shadow: none;
}
.mlib-enhance-panel .bbp-frequency-input::placeholder { color: var(--text-muted, #888); }
.mlib-enhance-panel .bbp-frequency-input:focus { outline: none; border-color: var(--color-accent, #ffd84b); }
/* Selects are deliberately left alone: .bws-dropdown is the app-wide dropdown
   (the Handpan "Type" picker is this same control), and its chevron rides in
   the `background` shorthand — so restyling that here would silently erase the
   arrow along with the fill. */
.mlib-enhance-panel .mlib-textarea { min-height: 84px; resize: vertical; }

/* Field captions match .prb-describe-label — accent, sentence case, 13/700 —
   replacing the 11px uppercase grey the library used on its own. */
.mlib-enhance-panel .mlib-field-label,
.mlib-enhance-panel .mlib-results-label {
    font-size: 13px; font-weight: 700; letter-spacing: 0.03em;
    text-transform: none; color: var(--color-accent, #ffd84b); margin-bottom: 8px;
}
.mlib-enhance-panel .mlib-field-label { display: block; }
/* Keeps its flex row — this label carries a leading icon. */
.mlib-enhance-panel .mlib-results-label { margin: 16px 0 8px; }

/* Grouped surfaces (advanced accordion, boosted-style preview, result cards)
   take the .prb-modules-list container: radius 8, 12px padding, dark-lighter
   on an accent hairline — replacing three different one-off greys. */
.mlib-enhance-panel .mlib-advanced-panel {
    padding: 12px; border-radius: 8px; margin: 0 0 10px;
    background: var(--bg-dark-lighter, #1a1a1a);
    border: 1px solid var(--border-accent-light, rgba(255,216,75,0.25));
}
/* Result cards keep their own padding — the thumbnail runs edge to edge inside
   them — so they take only the surface tokens, not the box. */
.mlib-enhance-panel .mlib-result-card {
    border-radius: 8px;
    background: var(--bg-dark-lighter, #1a1a1a);
    border: 1px solid var(--border-accent-light, rgba(255,216,75,0.25));
}
/* Transport bar: .hpp-player brings the whole look with it; it only needs to be
   inset from the card edge and stopped from stretching as a flex child. */
.mlib-enhance-panel .mlib-result-player { flex: none; margin: 0 0.75rem 0.7rem; }
/* The <audio> is now just the source for that bar — it carries no `controls`,
   but this keeps the old visible-player rules from reserving a 34px strip. */
.mlib-enhance-panel .mlib-result-audio { display: none; }
/* Save button: cloud glyph + label on one baseline. */
.mlib-enhance-panel .mlib-save-btn { display: inline-flex; align-items: center; gap: 0.4rem; }

/* Mode tabs are .prb-action-btn in the markup; `active` is the lit state, drawn
   like .prb-ai-btn so "the tab you are on" and "the button that acts" agree. */
.mlib-enhance-panel .mlib-mode-btn { min-width: 92px; opacity: 1; }
.mlib-enhance-panel .mlib-mode-btn.active {
    border-color: var(--color-accent, #ffd84b) !important;
    background: linear-gradient(135deg, rgba(255,216,75,0.18), rgba(255,216,75,0.06)) !important;
    color: var(--color-accent, #ffd84b) !important;
    box-shadow: none;
}
.mlib-enhance-panel .mlib-mode-row { display: flex; align-items: center; flex-wrap: wrap; gap: 10px; margin-bottom: 16px; }

/* Every slider is a .mlib-field-row now — caption above the track, exactly like
   Duration — so the group only has to stop adding gap on top of the rows' own
   bottom margin. The old inline .mlib-slider-label is gone from this module. */
.mlib-enhance-panel .mlib-slider-val { font-size: 13px; font-weight: 700; color: var(--color-accent, #ffd84b); }
.mlib-enhance-panel .mlib-slider-group { gap: 0; margin-bottom: 0; }
.mlib-enhance-panel .mlib-slider-group .mlib-field-row:last-child { margin-bottom: 0; }
/* Where fields follow the sliders (Custom and Extend put Exclude styles / Vocal
   preference under them), the block break is wider than the gap between two
   sliders — otherwise the last description looks like a caption for the field
   below it. No margin when the group ends the panel, as in Simple. */
.mlib-enhance-panel .mlib-slider-group:not(:last-child) { margin-bottom: 28px; }

/* A slider field is caption → track → description, and those three have to read
   as one unit: tight inside (4px), wide outside (20px). At equal spacing the
   description above binds to the next caption as readily as to its own slider.
   `:has` rather than a group class so Duration — which sits on its own, outside
   any advanced panel — spaces identically. */
.mlib-enhance-panel .mlib-field-row:has(.mlib-slider-row) {
    gap: 4px;
    margin-bottom: 20px;
}
.mlib-enhance-panel .mlib-field-row:has(.mlib-slider-row) > .mlib-field-label { margin-bottom: 0; }

/* "Advanced Options" disclosure — the builder's muted-to-accent text button. */
.mlib-enhance-panel .mlib-text-btn { font-size: 13px; font-weight: 700; color: var(--text-muted, #aaa); padding: 10px 0; }
.mlib-enhance-panel .mlib-advanced-header { margin: 4px 0 0; }

/* Action row + status. The status text becomes the builder's .prb-status box so
   progress reads as a panel, not as a caption stuck beside the button. */
/* The action row is layout only — no surface of its own. It used to carry
   .bbp-session-controls, which draws a filled, bordered box; once the design
   pass dropped its top border that box read as a broken three-sided frame. The
   button and the status panel are their own surfaces, so the row between them
   should draw nothing at all. */
.mlib-enhance-panel .mlib-generate-row {
    display: flex; flex-wrap: wrap; align-items: center; gap: 10px;
    margin-top: 16px; padding: 0; border: 0; background: none;
}
.mlib-enhance-panel .mlib-generate-btn { min-width: 0; padding: 10px 16px; font-size: 13px; font-weight: 700; }
.mlib-enhance-panel .mlib-status-row {
    flex: 1; min-width: 200px; gap: 0; padding: 10px 12px; border-radius: 6px;
    background: var(--bg-dark-lighter, #1a1a1a);
    border: 1px solid var(--border-accent-light, rgba(255,216,75,0.25));
}
.mlib-enhance-panel .mlib-status-text { font-size: 13px; line-height: 1.5; white-space: normal; }
.mlib-enhance-panel .mlib-spinner {
    width: 13px; height: 13px; margin-right: 8px;
    border: 2px solid var(--border-accent-light, rgba(255,216,75,0.25));
    border-top-color: var(--color-accent, #ffd84b);
}
.mlib-enhance-panel .mlib-elapsed { margin-left: auto; font-family: inherit; font-size: 13px; font-weight: 700; }

/* The panel's own frame already matches .bws-build-dialog (accent border,
   radius 6, 16px glow) — only the padding differed, and `relative` is what the
   absolutely-positioned .bws-build-close hangs off. */
.mlib-enhance-panel { position: relative; padding: 18px; }
.mlib-enhance-panel h3 { margin: 0 0 14px; font-size: 16px; font-weight: 700; color: #fff; line-height: 1.35; }

/* Admin note inside the save dialog (mounted on <body>) */
.prb-save-official-note {
    margin: -4px 0 12px; padding: 8px 10px; border-radius: 6px; font-size: 12px; line-height: 1.4;
    background: rgba(255,216,75,0.08); border: 1px solid rgba(255,216,75,0.3); color: var(--color-accent, #ffd84b);
}

.bws-x-btn,
.bws-x-close-button,
.bws-sim-close,
.bws-build-close,
.mlib-close-btn,
.mr-save-close,
.hpp-section-remove-btn,
.sdp-section-remove-btn,
.sgp-section-remove-btn,
.sbp-bim-close,
.sbp-ppm-close,
.alb-modal-close,
.bp-modal-close,
.fp-modal-close,
.ip-modal-close,
.alb-pl-remove,
.mlib-pl-remove,
.hpp-preset-dropdown .hpp-pd-close,
.ft-preset-dropdown .ft-pd-close,
.bt-preset-dropdown .bt-pd-close,
.it-preset-dropdown .it-pd-close,
.ft-info-modal .ft-im-close,
.bt-info-modal .bt-im-close,
.it-info-modal .it-im-close,
.hpp-bim-close,
.sdp-bim-close,
.sgp-bim-close,
.bws-usage-close,
.um-drawer-close,
.bws-x-btn--sm,
.mr-np-close {
    width: 26px; height: 26px; flex-shrink: 0; padding: 0;
    border: 0; border-radius: 50%; cursor: pointer; box-shadow: none;
    background: rgba(211,211,211,1) url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' width='24' height='24'%3E%3Cpath d='M18 6L6 18M6 6l12 12' stroke='%23141414' stroke-width='2.5' stroke-linecap='round'/%3E%3C/svg%3E") center/13px no-repeat;
    /* Any inline "✕" the markup still carries must not double up with the
       background glyph; the button keeps its own display value. */
    font-size: 0; line-height: 0; color: transparent;
    transition: all .2s ease;
}
.bws-x-btn > *,
.bws-x-close-button > *,
.bws-sim-close > *,
.bws-build-close > *,
.mlib-close-btn > *,
.mr-save-close > *,
.hpp-section-remove-btn > *,
.sdp-section-remove-btn > *,
.sgp-section-remove-btn > *,
.sbp-bim-close > *,
.sbp-ppm-close > *,
.alb-modal-close > *,
.bp-modal-close > *,
.fp-modal-close > *,
.ip-modal-close > *,
.alb-pl-remove > *,
.mlib-pl-remove > *,
.hpp-preset-dropdown .hpp-pd-close > *,
.ft-preset-dropdown .ft-pd-close > *,
.bt-preset-dropdown .bt-pd-close > *,
.it-preset-dropdown .it-pd-close > *,
.ft-info-modal .ft-im-close > *,
.bt-info-modal .bt-im-close > *,
.it-info-modal .it-im-close > *,
.hpp-bim-close > *,
.sdp-bim-close > *,
.sgp-bim-close > *,
.bws-usage-close > *,
.um-drawer-close > *,
.bws-x-btn--sm > *,
.mr-np-close > * { display: none; }

.bws-x-btn--sm,
.mr-np-close {
    width: 20px; height: 20px;
    background-size: 10px;
}

.bws-x-btn:hover:not(:disabled),
.bws-x-close-button:hover:not(:disabled),
.bws-sim-close:hover:not(:disabled),
.bws-build-close:hover:not(:disabled),
.mlib-close-btn:hover:not(:disabled),
.mr-save-close:hover:not(:disabled),
.hpp-section-remove-btn:hover:not(:disabled),
.sdp-section-remove-btn:hover:not(:disabled),
.sgp-section-remove-btn:hover:not(:disabled),
.sbp-bim-close:hover:not(:disabled),
.sbp-ppm-close:hover:not(:disabled),
.alb-modal-close:hover:not(:disabled),
.bp-modal-close:hover:not(:disabled),
.fp-modal-close:hover:not(:disabled),
.ip-modal-close:hover:not(:disabled),
.alb-pl-remove:hover:not(:disabled),
.mlib-pl-remove:hover:not(:disabled),
.hpp-preset-dropdown .hpp-pd-close:hover:not(:disabled),
.ft-preset-dropdown .ft-pd-close:hover:not(:disabled),
.bt-preset-dropdown .bt-pd-close:hover:not(:disabled),
.it-preset-dropdown .it-pd-close:hover:not(:disabled),
.ft-info-modal .ft-im-close:hover:not(:disabled),
.bt-info-modal .bt-im-close:hover:not(:disabled),
.it-info-modal .it-im-close:hover:not(:disabled),
.hpp-bim-close:hover:not(:disabled),
.sdp-bim-close:hover:not(:disabled),
.sgp-bim-close:hover:not(:disabled),
.bws-usage-close:hover:not(:disabled),
.um-drawer-close:hover:not(:disabled) {
    background: var(--color-danger, #dc3545) url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' width='24' height='24'%3E%3Cpath d='M18 6L6 18M6 6l12 12' stroke='%23FFFFFF' stroke-width='2.5' stroke-linecap='round'/%3E%3C/svg%3E") center/13px no-repeat;
    transform: none; box-shadow: none; color: transparent; border-color: transparent;
}
.bws-x-btn--sm:hover:not(:disabled),
.mr-np-close:hover:not(:disabled) {
    background: var(--color-danger, #dc3545) url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' width='24' height='24'%3E%3Cpath d='M18 6L6 18M6 6l12 12' stroke='%23FFFFFF' stroke-width='2.5' stroke-linecap='round'/%3E%3C/svg%3E") center/10px no-repeat;
    transform: none; box-shadow: none; color: transparent; border-color: transparent;
}

.bws-x-btn:disabled,
.bws-x-close-button:disabled,
.bws-x-close-button.bws-x-close-disabled,
.hpp-section-remove-btn:disabled,
.sdp-section-remove-btn:disabled,
.sgp-section-remove-btn:disabled,
.bws-x-btn--sm:disabled { opacity: 0.3; cursor: not-allowed; }

/* ── Modal breathing room ─────────────────────────────────────────────────────
   One constraint every modal in the app shares: a tall one stops 50px short of
   the top and bottom of the viewport rather than running into the edges. The
   panels already scroll internally past their own cap,
so this only decides
   where that cap sits.

   Each overlay declares its own padding (24px,
or 4-5vh on the two that scroll
   the overlay itself),
so the vertical half is restated here for all of them at
   once — and this block sits at the end of the stylesheet because several of
   those overlay rules appear later in the file than any shared block could.
   Horizontal padding is left alone: it is per-modal and 50px would crush a
   narrow screen.

   Adding a modal means adding its overlay class here and capping its panel at
   the same calc() — see the panel rules,
which now all read calc(100vh - 100px)
   instead of the assorted 80/82/88/92vh they had grown. */
   .alb-modal-overlay,
   .alv-info-overlay,
   .av2p-dialog-overlay,
   .bp-modal-overlay,
   .bt-info-overlay,
   .bws-playblock-overlay,
   .bws-sim-overlay,
   .dcp-modal-overlay,
   .fp-modal-overlay,
   .ft-info-overlay,
   .gcp-modal-overlay,
   .hcp-modal-overlay,
   .hpp-bim-overlay,
   .ip-modal-overlay,
   .it-info-overlay,
   .mr-modal-overlay,
   .sbp-bowl-info-overlay,
   .sbp-ppm-overlay,
   .scp-modal-overlay,
   .sdp-bim-overlay,
   .sgp-bim-overlay,
   .tin-info-overlay { padding-top: 50px; padding-bottom: 50px; }

/* Listed apart only because it is the one overlay whose default is `display:none`
   until it opens. Like the Media Recorder's save overlay above, it aligns its
   panel to the top and scrolls the OVERLAY rather than the panel — so here the
   bottom padding is doing real work: without it the last field sits flush
   against the viewport edge at the end of the scroll. */
.mlib-ai-modal-overlay { padding-top: 50px; padding-bottom: 50px; }

/* ── Session intensity curve editor ─────────────────────────────────────── */
.sbp-curve-wrap {
    position: relative;
    width: 100%;
    height: 190px;
    margin-top: 8px;
    border: 1px solid var(--border-accent-light);
    border-radius: 6px;
    background: var(--bg-dark-medium);
    overflow: hidden
}

.sbp-curve-canvas {
    display: block;
    width: 100%;
    height: 100%;
    cursor: crosshair;
    touch-action: none
}

.sbp-curve-disabled .sbp-curve-canvas {
    cursor: not-allowed
}

.sbp-curve-hint {
    position: absolute;
    inset: 0;
    display: flex;
    align-items: center;
    justify-content: center;
    font-size: 12px;
    color: var(--text-muted);
    background: var(--bg-dark-heavy);
    pointer-events: none;
    text-align: center;
    padding: 0 12px
}

.sbp-curve-footer {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 8px;
    margin-top: 8px
}

.sbp-curve-readout {
    font-size: 11px;
    color: var(--text-muted)
}

.sbp-curve-reset {
    padding: 4px 12px;
    font-size: 11px;
    flex: 0 0 auto
}

/* ── Admin panels: the two states that must not read as body text ────────────
   Used by Price calibration and AI Engines (js/bws-admin.js). The rest of those
   panels is deliberately plain; these two are not decoration — one says billing
   is wrong, the other says an engine answered. Theme tokens, so no light-theme
   counterpart is needed. */
.bws-cal-warn,
.bws-cal-ok {
    margin: 0.6rem 0;
    padding: 0.5rem 0.65rem;
    border: 1px solid;
    border-radius: 4px;
    font-size: 12px;
    line-height: 1.45;
}
.bws-cal-warn {
    color: var(--color-warning, #e65100);
    border-color: var(--color-warning, #e65100);
    background: var(--bg-danger, rgba(220, 53, 69, 0.16));
}
.bws-cal-ok {
    color: var(--color-success, #2e7d32);
    border-color: var(--color-success, #2e7d32);
    background: var(--bg-success, rgba(40, 167, 69, 0.18));
}
.bws-cal-hint { font-size: 12px; color: var(--text-muted, rgba(255,255,255,0.55)); line-height: 1.45; }
.bws-cal-label { display: block; margin: 0.6rem 0 0.25rem; font-size: 12px; color: var(--text-bright, rgba(255,255,255,0.8)); }
.bws-cal-actions { display: flex; gap: 0.5rem; margin: 0.7rem 0; }

/* The raw upstream reply in an AI Engines test — the thing that says WHY. */
.bws-cal-body {
    margin: 0.45rem 0 0;
    padding: 0.4rem 0.5rem;
    max-height: 9rem;
    overflow: auto;
    font-size: 11px;
    line-height: 1.4;
    white-space: pre-wrap;
    word-break: break-word;
    color: var(--text-bright, rgba(255,255,255,0.8));
    background: var(--bg-dark-heavy, rgba(0,0,0,0.3));
    border-radius: 3px;
}

/* Plain text input for the admin panels (AI Engines endpoint field). */
.bws-input {
    width: 100%;
    box-sizing: border-box;
    padding: 0.4rem 0.55rem;
    font-family: inherit;
    font-size: 12px;
    color: var(--text-white, #fff);
    background: var(--input-bg, #1a1a1a);
    border: 1px solid var(--border-accent-medium, rgba(255,216,75,.24));
    border-radius: 3px;
}
.bws-input:focus { outline: none; border-color: var(--color-accent, #ffd84b); }

/* ═══════════════════════════════════════════════════════════════════════════
   MEDIA EXPORTER MODULE (MEDIA-EXPORTER-MODULE/) — mex-*
   Offline render panel: mode + options, source checklist, progress.
   Lived in a <style> block inside the module's own index.html until 2026-09-03,
   which is why nothing in it followed the design tokens and none of it reached
   the generated light theme — every other module's CSS belongs here.
   ═══════════════════════════════════════════════════════════════════════════ */

#mex-module-content .mex-panel {
    display: flex;
    flex-direction: column;
    gap: 1rem;
    width: 100%;
}

#mex-module-content .mex-dur-auto {
    font-size: 12px;
    font-variant-numeric: tabular-nums;
    color: var(--color-accent);
    white-space: nowrap;
}

/* ── The export bar ─────────────────────────────────────────────────────
   Two padded strips in one card — the take's length above, what to make of it
   and the button that starts it below — which is the singing bowls session card
   (.sbp-session-card) exactly: same chrome, same split, same label-left duration
   field. It is the last child of .bws-sticky-wrap, so the shared bottom-bar rules
   pin it to the bottom of the screen while the sources it acts on scroll past.
   Those rules supply the pinned plate and the brand column; this is how it looks
   before it pins. */
/* Deliberately NOT scoped under #mex-module-content, unlike the rest of this
   section: the shared bottom-bar rules address it as `.bws-sticky-wrap > …`, and
   an id in front of the class here would out-specify them — the bar would keep
   this resting chrome instead of the pinned plate, and never stick at all.
   .sbp-session-card is written the same way, for the same reason. */
.mex-console-box {
    background: var(--bg-dark-light);
    border: 1px solid var(--border-accent-light);
    border-radius: 4px;
}
/* Length first, on its own strip — the bowls card's upper section. */
#mex-module-content .mex-controls-top {
    display: flex; align-items: center; justify-content: space-between;
    gap: 1.5rem; padding: .75rem;
    border-bottom: 1px solid var(--border-accent-light);
}
/* The console, its note, and the progress + status the button reports through:
   a column, because they stack under the row rather than sit in it. */
#mex-module-content .mex-controls-bottom {
    display: flex; flex-direction: column; gap: .6rem; padding: .6rem .75rem;
}
#mex-module-content .mex-ctrl-label {
    font-size: 13px; color: var(--color-accent); font-weight: 400;
    text-transform: uppercase; letter-spacing: .5px; white-space: nowrap; flex: 0 0 auto;
}
#mex-module-content .mex-duration-group { display: flex; align-items: center; gap: .6rem }

/* ── Console — one action row ───────────────────────────────────
   What the render produces, the settings for that choice, and the button that
   starts it. Deliberately the same shape as the recorder's .mr-console (which
   these rules mirror rather than borrow — mex-* keeps this module's CSS its
   own): every field carries its label above it, so the row aligns on the field
   baseline rather than on the middle of the boxes. It stays on the app accent,
   where the recorder's console re-points itself to record red — an export is
   not a take being captured. */
#mex-module-content .mex-console {
    display: flex;
    align-items: flex-end;
    flex-wrap: wrap;
    gap: 10px;
}
#mex-module-content .mex-console-spacer { flex: 1 1 auto; }
#mex-module-content .mex-console-btn { flex: 0 0 auto; white-space: nowrap; text-align: center; margin-top: 0; }

/* Export mode — the field the rest of the row answers to, so it leads and keeps
   one width whichever mode is picked. Sized to "Video + Audio", the longer of the
   two, so the row never reflows on a change of mode. */
#mex-module-content .mex-mode-field {
    flex: 0 0 150px;
    box-sizing: border-box;
    width: 150px; min-width: 150px; max-width: 150px;
}

/* The settings for the selected mode take the middle of the row; the spacer
   after them pushes the export button to the right edge. */
#mex-module-content .mex-opts {
    display: flex;
    align-items: flex-end;
    flex-wrap: wrap;
    gap: 12px;
    flex: 1 1 auto;
    min-width: 0;
}
#mex-module-content .mex-opt { flex: 1 1 130px; min-width: 0; }
#mex-module-content .mex-opt > span {
    display: block;
    margin-bottom: 4px;
    font-size: 11px;
    font-weight: 700;
    letter-spacing: 0.04em;
    text-transform: uppercase;
    color: var(--text-muted);
}
#mex-module-content .mex-opt .bws-dropdown { width: 100%; }
#mex-module-content .mex-opt-fps { flex: 0 0 92px; }
#mex-module-content .mex-opt-abr { flex: 0 0 150px; }
#mex-module-content .mex-opt-num { flex: 0 0 auto; }

/* Each custom size field carries its own label, so the pair aligns on the field
   edge like every other labelled field in the row; the × sits with them. */
#mex-module-content .mex-size-custom { display: inline-flex; align-items: flex-end; gap: 6px; flex: 0 0 auto; }
/* .bbp-frequency-input defaults to 34px tall, 11px type, right-aligned — all
   three matched to the selects beside it, so the custom size fields read as the
   same kind of field: same edges, same type size, text starting at the left. */
#mex-module-content .mex-size-num {
    width: 104px; height: 38px;
    padding: 0 10px;
    font-size: 13px;
    text-align: left;
}
#mex-module-content .mex-size-x { color: var(--text-muted); padding-bottom: 10px; }

/* Settings describe the take, so they are inert while one renders. */
#mex-module-content .mex-console .bws-dropdown:disabled,
#mex-module-content .mex-console .bbp-frequency-input:disabled {
    opacity: 0.4; cursor: not-allowed;
}

/* What this mode is about to do — wraps to its own line under the row it
   describes, which is why it is inside the console rather than beside it. */
#mex-module-content .mex-console-note {
    flex: 1 1 100%;
    margin: 2px 0 0;
    font-size: 11px;
    line-height: 1.45;
    color: var(--text-muted);
}

/* Narrow: fields stack and the buttons take the full width rather than wrapping
   raggedly, matching the recorder's own breakpoints. */
@media (max-width: 620px) {
    #mex-module-content .mex-console-spacer { display: none; }
    #mex-module-content .mex-console-btn { flex: 1 1 160px; }
}
@media (max-width: 520px) {
    #mex-module-content .mex-mode-field { flex: 1 1 100%; width: 100%; max-width: 100%; }
    #mex-module-content .mex-opt { flex: 1 1 100%; }
    #mex-module-content .mex-opt-num { flex: 0 0 auto; }
}

/* ── Source checklist ───────────────────────────────────────────────────── */
#mex-module-content .mex-sources {
    background: var(--bg-dark-heavy);
    border: 1px solid var(--border-accent-medium);
    border-radius: 4px;
    padding: .75rem 1rem;
}
#mex-module-content .mex-sources-head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 1rem;
    margin-bottom: .5rem;
}
#mex-module-content .mex-src-row {
    display: flex;
    align-items: center;
    gap: .6rem;
    padding: .3rem 0;
    font-size: 13px;
    cursor: pointer;
}
#mex-module-content .mex-src-row input[type="checkbox"] {
    accent-color: var(--color-accent);
    flex: 0 0 auto;
    margin: 0;
    cursor: pointer;
}
/* A source the recorder knows but that has no renderOffline port yet. */
#mex-module-content .mex-src-liveonly { opacity: .45; cursor: default; }
#mex-module-content .mex-src-liveonly input[type="checkbox"] { cursor: default; }
#mex-module-content .mex-src-name { flex: 1 1 auto; color: var(--text-bright); }
#mex-module-content .mex-src-badge {
    font-size: 10px;
    text-transform: uppercase;
    letter-spacing: .5px;
    padding: .1rem .5rem;
    border-radius: 2px;
    border: 1px solid var(--color-border);
    color: var(--text-muted);
    white-space: nowrap;
}
#mex-module-content .mex-badge-ok {
    border-color: var(--color-accent);
    color: var(--color-accent);
    background: var(--bg-accent-light);
}
#mex-module-content .mex-src-dur {
    font-size: 12px;
    font-variant-numeric: tabular-nums;
    color: var(--text-muted);
    min-width: 4.5rem;
    text-align: right;
}
#mex-module-content .mex-src-empty {
    font-size: 12px;
    color: var(--text-muted);
    padding: .4rem 0;
}

/* Refresh: the neutral accent button (.bws-button-a-v3) at the reduced size the
   header row needs — the base 120px min-width would dwarf the label beside it. */
#mex-module-content .mex-btn-small {
    min-width: 0;
    padding: .35rem .9rem;
    font-size: 11px;
}

/* ── Progress + status  ─────────────────────────────────────────────────── */
#mex-module-content .mex-progress { display: flex; flex-direction: column; gap: .4rem; }
#mex-module-content .mex-progress-track {
    height: 8px;
    border-radius: 2px;
    background: var(--input-bg);
    border: 1px solid var(--border-accent-medium);
    overflow: hidden;
}
#mex-module-content .mex-progress-fill {
    height: 100%;
    width: 0;
    background: var(--color-accent);
    box-shadow: 0 0 10px var(--glow-accent);
    transition: width .2s linear;
}
#mex-module-content .mex-progress-text {
    font-size: 12px;
    font-variant-numeric: tabular-nums;
    color: var(--text-bright);
}

#mex-module-content .mex-status {
    min-height: 1.2em;
    font-size: 12px;
    color: var(--text-muted);
}

/* ═══════════════════════════════════════════════════════════════════════════
   AUDIO LEVELER MODULE (AUDIO-LEVELER-MODULE/) — alv-*
   Source picker, before/after meters, waveform + A/B transport, controls, save.
   Everything is scoped under #alv-module-content so nothing here can reach a
   neighbouring module, the way the mex-* block is written.
   ═══════════════════════════════════════════════════════════════════════════ */

#alv-module-content .alv-panel {
    display: flex;
    flex-direction: column;
    gap: 1rem;
    width: 100%;
}

/* Every section of this module is one of these boxes, so the page reads as a
   sequence of steps rather than a wall of controls. Same chrome as the
   exporter's .mex-console-box. */
#alv-module-content .alv-box {
    background: var(--bg-dark-light);
    border: 1px solid var(--border-accent-light);
    border-radius: 4px;
    padding: .75rem;
    display: flex;
    flex-wrap: wrap;
    align-items: flex-end;
    gap: 12px;
}
#alv-module-content .alv-box-head {
    flex: 1 0 100%;
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: .75rem;
}
#alv-module-content .alv-note {
    font-size: 12px;
    color: var(--text-muted);
}

/* Panes and boxes in this module are flex containers, and a bare `display:
   flex` beats the UA rule behind the `hidden` attribute — without this every
   hidden step (all three source panes at once) stays on screen. */
#alv-module-content [hidden] { display: none !important; }

/* ── Source tabs ─────────────────────────────────────────────────────────
   Three places a clip can come from. Segmented pills, the same shape as the
   Media Library section tabs, so a source picker looks like every other one
   in the app. They switch the row beneath them, they do not run anything. */
#alv-module-content .alv-tabs {
    flex: 1 0 100%;
    display: flex;
    gap: 8px;
}
#alv-module-content .alv-tab {
    appearance: none;
    flex: 1;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: 0.4rem;
    padding: 0.6rem 1rem;
    border-radius: 8px;
    cursor: pointer;
    background: var(--bg-dark-lighter, #1a1a1a);
    color: var(--text-muted, #aaa);
    border: 1px solid var(--border-accent-light, rgba(255,216,75,0.25));
    font-size: 14px;
    font-weight: 600;
    font-family: inherit;
    transition: background .2s ease, color .2s ease, border-color .2s ease;
}
#alv-module-content .alv-tab:hover {
    border-color: var(--color-accent, #ffd84b);
    color: var(--color-accent, #ffd84b);
}
#alv-module-content .alv-tab.active {
    background: var(--color-accent, #ffd84b);
    color: #111;
    border-color: var(--color-accent, #ffd84b);
}
#alv-module-content .alv-src-pane {
    flex: 1 0 100%;
    display: flex;
    flex-wrap: wrap;
    align-items: flex-end;
    gap: 12px;
}

/* Labelled field, the exporter's .mex-opt shape: the label sits above its
   control so a row of mixed field widths still aligns on the field edge. */
#alv-module-content .alv-field { flex: 0 1 150px; min-width: 0; }
#alv-module-content .alv-field-wide { flex: 1 1 220px; }
#alv-module-content .alv-field > span {
    display: block;
    margin-bottom: 4px;
    font-size: 11px;
    font-weight: 700;
    letter-spacing: .04em;
    text-transform: uppercase;
    color: var(--text-muted);
}
#alv-module-content .alv-field .bws-dropdown,
#alv-module-content .alv-field .bws-input { width: 100%; }

/* An expert field whose control has an "i" beside it: the wrapper the button is
   mounted into. The control gives up its width: 100% and takes what is left of
   the field once the badge has its 20px.

   Matched as `> span.alv-field-row` rather than on the class alone: the wrapper
   is itself a direct-child span of the field, so the label rule above would
   otherwise out-specify it and turn the row back into a block. */
#alv-module-content .alv-field > span.alv-field-row {
    display: flex;
    align-items: center;
    gap: 6px;
}
#alv-module-content .alv-field > span.alv-field-row > .bws-dropdown,
#alv-module-content .alv-field > span.alv-field-row > .bws-input {
    flex: 1 1 auto;
    width: auto;
    min-width: 0;
}

/* Geometry only — the accent/hover colours come from .bws-info-badge, and it is
   shrunk from that badge's 24px so it reads as a companion to the field rather
   than as a control in its own right. */
#alv-module-content .alv-info-btn {
    width: 20px;
    height: 20px;
    font-size: 13px;
}

/* ── Expert control info modal ────────────────────────────────────────────
   Mounted on <body>, so these rules are unscoped. Same shell as every other
   info modal in the app (.sbp-bowl-info-*): the scrim, the panel glass and the
   vertical padding cap are the three shared lists this class is registered in,
   and the close button is the shared .bws-x-btn — so only layout lives here. */
.alv-info-overlay {
    position: fixed;
    inset: 0;
    z-index: 9000;
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 24px;
    overflow: hidden;
    font-family: 'Roboto', -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
}
.alv-info-modal {
    width: 100%;
    max-width: 520px;
    max-height: calc(100vh - 100px);
    display: flex;
    flex-direction: column;
    border: 1px solid var(--color-accent, #ffd84b);
    border-radius: 8px;
    box-shadow: 0 0 12px rgba(255,216,75,0.4), 0 8px 32px rgba(0,0,0,0.7);
    overflow: hidden;
}
.alv-im-head {
    display: flex;
    align-items: center;
    gap: 0.6rem;
    padding: 1rem 1rem 0.75rem;
    flex-shrink: 0;
}
/* .alv-im-badge look comes from .bws-info-badge */
.alv-im-title {
    flex: 1;
    color: var(--text-primary, #fff);
    font-size: 16px;
    font-weight: 700;
}
.alv-im-underline {
    height: 1px;
    background: rgba(128,128,128,0.4);
    margin: 0 1rem;
    flex-shrink: 0;
}
.alv-im-body {
    overflow-y: auto;
    padding: 1rem;
    scrollbar-width: thin;
    scrollbar-color: var(--color-accent, #ffd84b) #000;
}
.alv-im-body::-webkit-scrollbar { width: 8px; }
.alv-im-body::-webkit-scrollbar-track { background: #000; }
.alv-im-body::-webkit-scrollbar-thumb { background: var(--color-accent, #ffd84b); border-radius: 4px; }
.alv-im-p {
    margin: 0 0 0.75rem;
    color: rgba(248,249,250,0.92);
    font-size: 14px;
    line-height: 1.6;
}
.alv-im-p:last-of-type { margin-bottom: 0; }
/* The copy names dropdown options inline; white lifts them out of the body
   text without turning the paragraph into a list. */
.alv-im-p b { color: var(--text-primary, #fff); font-weight: 700; }
/* The value the Reset button puts back, so "what did I change?" is answerable
   from inside the explanation. */
.alv-im-default {
    margin-top: 12px;
    padding: 0.5rem 0.625rem;
    border-radius: 4px;
    background: rgba(255,216,75,0.05);
    border: 1px solid rgba(255,216,75,0.12);
    font-size: 13px;
    color: rgba(248,249,250,0.92);
}
.alv-im-default-label { color: var(--color-accent, #ffd84b); font-weight: 600; }

@media (max-width: 560px) {
    .alv-info-overlay { padding: 10px; }
}
#alv-module-content .alv-file-input {
    width: 100%;
    font-size: 12px;
    color: var(--text-muted);
}
#alv-module-content .alv-btn { flex: 0 0 auto; white-space: nowrap; margin-top: 0; }
#alv-module-content .alv-btn-go { min-width: 120px; }
#alv-module-content .alv-btn-quiet { opacity: .8; font-weight: 400; }
#alv-module-content .alv-btn-danger { border-color: rgba(220, 53, 69, .55); color: #ff8896; }
#alv-module-content .alv-btn-danger:hover { background: rgba(220, 53, 69, .18); }

#alv-module-content .alv-loaded {
    flex: 1 0 100%;
    display: flex;
    align-items: baseline;
    gap: .6rem;
    flex-wrap: wrap;
    padding-top: .4rem;
    border-top: 1px solid var(--border-accent-light);
}
#alv-module-content .alv-loaded-name { font-size: 14px; color: var(--color-accent); }
#alv-module-content .alv-loaded-meta { font-size: 12px; color: var(--text-muted); font-variant-numeric: tabular-nums; }

/* ── Before / after table ────────────────────────────────────────────────
   Two columns of the same four numbers. Tabular figures so the digits line up
   between the columns and a change of 0.1 dB is visible as a change, not as a
   reflow. */
#alv-module-content .alv-readout-box { display: block; }
#alv-module-content table.alv-readout {
    width: 100%;
    border-collapse: collapse;
    font-size: 13px;
    margin-top: .4rem;
}
#alv-module-content table.alv-readout th,
#alv-module-content table.alv-readout td {
    text-align: left;
    padding: .3rem .5rem .3rem 0;
    border-bottom: 1px solid var(--border-accent-light);
}
#alv-module-content table.alv-readout thead th {
    font-size: 11px;
    font-weight: 700;
    letter-spacing: .04em;
    text-transform: uppercase;
    color: var(--text-muted);
}
#alv-module-content table.alv-readout tbody th {
    font-weight: 400;
    color: var(--text-muted);
    width: 11rem;
}
#alv-module-content table.alv-readout td {
    font-variant-numeric: tabular-nums;
    width: 6rem;
}
#alv-module-content table.alv-readout td:nth-child(3) { color: var(--color-accent); }
#alv-module-content .alv-rt-note {
    width: auto !important;
    font-size: 11px;
    color: var(--text-muted);
}

/* What the render actually did, as chips — read after the fact, so they are
   annotations on the table above rather than controls. */
#alv-module-content .alv-moves { display: flex; flex-wrap: wrap; gap: 8px; margin-top: .6rem; }
#alv-module-content .alv-chip {
    display: inline-flex;
    align-items: baseline;
    gap: .4rem;
    padding: .2rem .5rem;
    border: 1px solid var(--border-accent-light);
    border-radius: 3px;
    font-size: 12px;
}
#alv-module-content .alv-chip-k { color: var(--text-muted); text-transform: uppercase; font-size: 10px; letter-spacing: .04em; }
#alv-module-content .alv-chip-v { color: var(--color-accent); font-variant-numeric: tabular-nums; }

/* ── Waveform ────────────────────────────────────────────────────────────
   Both versions share one set of axes; the canvas is sized in CSS and the
   backing store in JS at devicePixelRatio, so the trace stays crisp. */
#alv-module-content .alv-wave-box { display: block; }
#alv-module-content .alv-canvas {
    display: block;
    width: 100%;
    /* Must match CANVAS_H in js/audio-leveler.js — the canvas sizes its backing
       store from clientWidth but takes its height from the constant, so a
       disagreement here crops the ruler rather than scaling anything. */
    height: 270px;
    cursor: crosshair;
    background: rgba(0, 0, 0, .25);
    border-radius: 3px;
    touch-action: none;          /* pointer drags must not scroll the page */
}
/* The pan window below the canvas borrows the guided-meditation editor's
   .gmx-scroll wholesale, so the two modules cannot drift apart visually. Only
   the gutter inset is set here, from --gmx-axis. */
#alv-module-content .alv-scroll { margin-top: 4px; }
#alv-module-content .alv-zoomlbl {
    min-width: 42px;
    text-align: center;
    font-size: 11px;
    font-variant-numeric: tabular-nums;
    color: var(--text-muted);
}
/* The minus and plus are single glyphs; without a floor they come out narrower
   than every other button in the row. */
#alv-module-content .alv-zbtn { min-width: 34px; padding-left: .5rem; padding-right: .5rem; }
#alv-module-content .alv-transport {
    display: flex;
    align-items: center;
    gap: 12px;
    flex-wrap: wrap;
    margin-top: .6rem;
}
/* A/B as one segmented control: the two halves are the same decision, and a
   gap between them would read as two separate buttons. */
#alv-module-content .alv-ab {
    display: inline-flex;
    border: 1px solid var(--border-accent-light);
    border-radius: 3px;
    overflow: hidden;
}
#alv-module-content .alv-ab-btn {
    appearance: none;
    background: none;
    border: none;
    color: var(--text-muted);
    font-size: 12px;
    padding: .4rem .8rem;
    cursor: pointer;
}
#alv-module-content .alv-ab-btn:disabled { opacity: .4; cursor: default; }
#alv-module-content .alv-ab-btn.active { background: var(--color-accent); color: #000; }
#alv-module-content .alv-check { display: inline-flex; align-items: center; gap: .35rem; font-size: 12px; color: var(--text-muted); }
#alv-module-content .alv-time { margin-left: auto; font-size: 12px; font-variant-numeric: tabular-nums; color: var(--color-accent); }

/* ── Controls ────────────────────────────────────────────────────────────
   The two that do the job get a row each, with room for the sentence that says
   what they mean; the rest are behind the expert toggle. */
/* ── Auto adjust row ──────────────────────────────────────────────────────
   Clip type + the two buttons on one line, above the sliders they write to.
   Auto lives here rather than in the expert panel because it is the opposite of
   an expert control: it is what you press instead of opening that panel. */
#alv-module-content .alv-auto-row {
    flex: 1 0 100%;
    display: flex;
    flex-wrap: wrap;
    align-items: flex-end;
    gap: 12px;
}
#alv-module-content .alv-auto-row .alv-field-wide { flex: 1 1 200px; }

/* What Auto did and where each number came from. A button that silently moves
   eight fields is not trustworthy; this is the receipt. */
#alv-module-content .alv-auto-note {
    flex: 1 0 100%;
    display: flex;
    flex-wrap: wrap;
    gap: 6px;
    margin-top: -4px;
}
#alv-module-content .alv-auto-bit {
    padding: .15rem .45rem;
    border-radius: 3px;
    background: rgba(255, 216, 75, .07);
    border: 1px solid rgba(255, 216, 75, .18);
    font-size: 11px;
    line-height: 1.4;
    color: var(--text-muted);
}
#alv-module-content .alv-auto-bit:first-child {
    color: var(--color-accent);
    font-weight: 700;
}

#alv-module-content .alv-main-controls {
    flex: 1 0 100%;
    display: flex;
    flex-wrap: wrap;
    gap: 1.25rem;
}
#alv-module-content .alv-ctrl { flex: 1 1 280px; min-width: 0; }
#alv-module-content .alv-ctrl-label {
    display: block;
    font-size: 13px;
    color: var(--color-accent);
    text-transform: uppercase;
    letter-spacing: .5px;
    margin-bottom: .4rem;
}
#alv-module-content .alv-slider-row { display: flex; align-items: center; gap: .6rem; }
#alv-module-content .alv-slider-row .bws-volume-slider { flex: 1 1 auto; min-width: 80px; }
#alv-module-content .alv-num { flex: 0 0 74px; width: 74px; text-align: right; font-variant-numeric: tabular-nums; }
#alv-module-content .alv-unit { flex: 0 0 auto; font-size: 11px; color: var(--text-muted); }
#alv-module-content .alv-help {
    display: block;
    margin-top: .35rem;
    font-size: 11px;
    line-height: 1.45;
    color: var(--text-muted);
}
#alv-module-content .alv-help-wide { flex: 1 0 100%; margin-top: 0; }

#alv-module-content .alv-expert-toggle {
    flex: 1 0 100%;
    display: flex;
    align-items: center;
    gap: .35rem;
    appearance: none;
    background: none;
    border: none;
    padding: .3rem 0;
    color: var(--text-muted);
    font-size: 12px;
    text-transform: uppercase;
    letter-spacing: .04em;
    cursor: pointer;
}
#alv-module-content .alv-expert-toggle:hover { color: var(--color-accent); }
#alv-module-content .alv-expert-toggle svg { transition: transform .15s ease; }
#alv-module-content .alv-expert-toggle.open svg { transform: rotate(180deg); }

#alv-module-content .alv-expert {
    flex: 1 0 100%;
    display: flex;
    flex-direction: column;
    gap: .75rem;
    padding: .6rem 0 0;
    border-top: 1px solid var(--border-accent-light);
}
#alv-module-content .alv-expert-group {
    display: flex;
    flex-wrap: wrap;
    align-items: flex-end;
    gap: 10px;
}
#alv-module-content .alv-group-title {
    flex: 1 0 100%;
    font-size: 11px;
    font-weight: 700;
    letter-spacing: .06em;
    text-transform: uppercase;
    color: var(--color-accent);
}

/* ── Run ─────────────────────────────────────────────────────────────────
   Button, progress and status on one line: a render reports through all three
   and they belong to the same act. */
#alv-module-content .alv-run {
    flex: 1 0 100%;
    display: flex;
    align-items: center;
    gap: 12px;
    flex-wrap: wrap;
    padding-top: .6rem;
    border-top: 1px solid var(--border-accent-light);
}
#alv-module-content .alv-progress { flex: 1 1 220px; display: flex; align-items: center; gap: .6rem; min-width: 0; }
#alv-module-content .alv-progress-bar {
    flex: 1 1 auto;
    height: 6px;
    background: rgba(0, 0, 0, .45);
    border-radius: 3px;
    overflow: hidden;
}
#alv-module-content .alv-progress-fill { height: 100%; width: 0; background: var(--color-accent); transition: width .12s linear; }
#alv-module-content .alv-progress-text { flex: 0 0 auto; font-size: 11px; color: var(--text-muted); font-variant-numeric: tabular-nums; }
#alv-module-content .alv-status { flex: 1 1 100%; font-size: 12px; color: var(--text-muted); min-height: 1.2em; }
#alv-module-content .alv-status-ok    { color: var(--color-accent); }
#alv-module-content .alv-status-warn  { color: #ffb347; }
#alv-module-content .alv-status-error { color: #ff8896; }

/* Predicted output size. Turns warning-coloured when it will not fit through the
   upload limit, because at that point it is not an aside — it is the reason the
   Save button is about to refuse. */
#alv-module-content .alv-size-note { flex: 1 0 100%; }
#alv-module-content .alv-size-note.alv-over { color: #ffb347; }

#alv-module-content .alv-save-row {
    flex: 1 0 100%;
    display: flex;
    flex-wrap: wrap;
    align-items: flex-end;
    gap: 12px;
}

/* ── Backups ─────────────────────────────────────────────────────────────
   One row per kept version. The row belonging to the clip currently loaded is
   marked, because that is the one being decided about. */
#alv-module-content .alv-backup-list { flex: 1 0 100%; display: flex; flex-direction: column; gap: 6px; }
#alv-module-content .alv-backup {
    display: flex;
    align-items: center;
    gap: 10px;
    flex-wrap: wrap;
    padding: .45rem .6rem;
    border: 1px solid var(--border-accent-light);
    border-radius: 3px;
}
#alv-module-content .alv-backup-here { border-color: var(--color-accent); }
#alv-module-content .alv-backup-name { flex: 1 1 200px; font-size: 13px; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
#alv-module-content .alv-backup-meta { flex: 0 0 auto; font-size: 11px; color: var(--text-muted); font-variant-numeric: tabular-nums; }

/* Backup row on a Media Library track card. Present only on a track the Audio
   Leveler has replaced, filled in from AudioLevelerModule.backupForTrack(); the
   card asks again whenever the leveler announces a new backup list, so the row
   appears without a reload after a replace. Lives here rather than in the
   mlib-* block because the feature it belongs to is the leveler's. */
#mlib-module-content .mlib-rc-backup {
    display: flex;
    align-items: center;
    gap: .4rem;
    flex-wrap: wrap;
    margin-top: .35rem;
    font-size: 11px;
    color: var(--text-muted);
}
#mlib-module-content .mlib-rc-backup button {
    appearance: none;
    background: none;
    border: 1px solid var(--border-accent-light);
    border-radius: 3px;
    color: var(--text-muted);
    font-size: 10px;
    padding: .1rem .35rem;
    cursor: pointer;
}
#mlib-module-content .mlib-rc-backup button:hover { color: var(--color-accent); border-color: var(--color-accent); }
#mlib-module-content .mlib-rc-backup .mlib-backup-del { border-color: rgba(220, 53, 69, .5); color: #ff8896; }
#mlib-module-content .mlib-rc-backup .mlib-backup-del:hover { background: rgba(220, 53, 69, .18); border-color: rgba(220, 53, 69, .8); color: #ff8896; }

@media (max-width: 720px) {
    #alv-module-content .alv-field,
    #alv-module-content .alv-field-wide { flex: 1 0 100%; }
    #alv-module-content .alv-time { margin-left: 0; }
}

/* ══════════════════════════════════════════════════════════════════════════
   Guided Meditations — Voices tab (GUIDED-MEDITATIONS-MODULE/js/gm-voices.js)
   ══════════════════════════════════════════════════════════════════════════
   The reference recordings the Creator speaks a script in. The cards are the
   module's own .plp-pcard, so they sit in the grid at the same size and shape
   as the meditations and playlists in the tabs beside them; everything here is
   the difference — a voice has no cover art, and it has a badge saying whose
   it is. */

/* No artwork to show, so the cover is a glyph on the placeholder ground rather
   than a picture that failed to arrive. */
.gm-vcard-cover {
    display: flex; align-items: center; justify-content: center;
}
.gm-vcard-cover .gm-vcard-glyph {
    width: 38%; height: auto; max-width: 64px;
    color: var(--color-accent, #ffd84b); opacity: .55;
}

/* OFFICIAL / PERSONAL. Loud enough to read at a glance from across the grid,
   because it decides what the card lets you do: the curated ones are everyone's
   and read-only, your own are yours to rename and delete. */
.gm-vbadge {
    font-size: 9px; font-weight: 700; letter-spacing: .09em;
    padding: 2px 7px; border-radius: 3px;
}
.gm-vbadge-official {
    color: #0d0d0d; background: var(--color-accent, #ffd84b);
    border-color: var(--color-accent, #ffd84b);
}
.gm-vbadge-personal {
    color: var(--color-accent, #ffd84b); background: transparent;
    border: 1px solid var(--border-accent-light, rgba(255,216,75,0.35));
}

/* Up to five controls on a personal card, so they wrap rather than squeeze. */
.gm-vcard-actions { flex-wrap: wrap; gap: 6px; }
.gm-vcard-actions .prb-action-btn { padding: 6px 10px; font-size: 11px; }

/* Preview is the one control every card has, and the only one that changes
   state while you look at it, so it gets the outlined-to-filled treatment. */
.gm-vcard-preview {
    display: inline-flex; align-items: center; gap: 5px;
    padding: 6px 11px; border-radius: 6px; cursor: pointer;
    font-size: 11px; font-weight: 600; font-family: inherit;
    color: var(--color-accent, #ffd84b);
    background: var(--bg-dark-lighter, #1a1a1a);
    border: 1px solid var(--border-accent-light, rgba(255,216,75,0.25));
    transition: background-color .15s ease, color .15s ease, border-color .15s ease;
}
.gm-vcard-preview:hover { border-color: var(--color-accent, #ffd84b); }
.gm-vcard-preview:disabled { opacity: .55; cursor: default; }
.gm-vcard-preview.gm-vcard-playing {
    color: #0d0d0d; background: var(--color-accent, #ffd84b); border-color: var(--color-accent, #ffd84b);
}

/* ── The Creator's reference-voice picker ────────────────────────────────────
   A listbox standing in for a <select>, because each row carries a play button
   and a native <option> renders text and a tick and nothing else. The <select>
   is still there, hidden, still holding the value — see gm-ai-meditation.js. */
/* Hidden, not removed: it still holds the value. `select` in the selector
   because the @supports(base-select) block above sets display:flex on
   select.bws-dropdown, and a bare class loses that specificity contest. */
select.gm-vsel-native { display: none; }
.gm-vsel { position: relative; }

/* The trigger wears .bws-dropdown, so the height, border, colours, the shared
   ::after caret and the light theme all come from there rather than from a
   second copy here. Only the layout the class does not give a <button> is set:
   it has to be a flex row so the label can ellipsis inside it. */
.gm-vsel-trigger {
    display: flex;
    align-items: center;
    width: 100%;
    text-align: left;
    font-family: inherit;
    font-size: 13px;
}
.gm-vsel-current { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

/* Floats over what follows it rather than pushing the step open: the rows below
   are the file picker, and a list that shoved them down every time it opened
   would move the thing you were reaching for. */
.gm-vsel-menu {
    position: absolute;
    z-index: 30;
    top: calc(100% + 2px);
    left: 0;
    right: 0;
    max-height: 264px;
    overflow-y: auto;
    /* The app's popup-list surface, the same one .bt-preset-dropdown and the
       Singing Bowls bowl-type popup use. An opaque near-black, deliberately:
       --bg-dark-light is a translucent overlay token and a list floating over
       the fields below it has to actually cover them. */
    background: rgba(8, 8, 8, 0.98);
    border: 1px solid var(--color-accent, #ffd84b);
    border-radius: 5px;
    box-shadow: 0 0 10px var(--glow-accent), 0 8px 24px rgba(0, 0, 0, 0.6);
    font-family: 'Roboto', -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
}
.gm-vsel-group {
    padding: 7px 10px 4px;
    font-size: 10px;
    font-weight: 700;
    letter-spacing: 0.06em;
    text-transform: uppercase;
    color: var(--text-muted, rgba(255, 255, 255, 0.5));
}
.gm-vsel-opt {
    display: flex;
    align-items: center;
    gap: 8px;
    padding: 0.5rem 0.75rem;
    border-bottom: 1px solid rgba(255, 216, 75, 0.07);
    font-size: 13px;
    color: var(--text-bright, rgba(255, 255, 255, 0.85));
    cursor: pointer;
}
.gm-vsel-opt:last-child { border-bottom: 0; }
.gm-vsel-opt:hover { background: rgba(255, 216, 75, 0.1); }
.gm-vsel-opt.gm-vsel-selected {
    background: rgba(255, 216, 75, 0.16);
    color: var(--color-accent, #ffd84b);
}
/* The own-file row has no voice to preview, so it takes the play button's width
   as padding rather than pretending to a control it does not have. */
.gm-vsel-opt--own { padding-left: 2.6rem; }
.gm-vsel-name { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

.gm-vsel-play {
    flex: 0 0 auto;
    display: flex;
    align-items: center;
    justify-content: center;
    width: 22px;
    height: 22px;
    padding: 0;
    border-radius: 50%;
    border: 1px solid var(--border-accent-light, rgba(255, 216, 75, 0.25));
    background: var(--bg-dark-lighter, #1a1a1a);
    color: var(--color-accent, #ffd84b);
    cursor: pointer;
    transition: background-color .15s ease, color .15s ease, border-color .15s ease;
}
.gm-vsel-play:hover { border-color: var(--color-accent, #ffd84b); }
.gm-vsel-play:disabled { opacity: .55; cursor: default; }
.gm-vsel-play.gm-vsel-playing {
    color: #0d0d0d;
    background: var(--color-accent, #ffd84b);
    border-color: var(--color-accent, #ffd84b);
}

/* The entry card, in the shape every other entry card in the app wears: the
   whole tile is the button, and its two gold parts brighten together on hover.
   The inner button is inert so it can never take the hover or the focus alone. */
.gm-vcard-add { cursor: pointer; }
.gm-vcard-add .bws-card-cover--custom {
    display: flex; align-items: center; justify-content: center; overflow: hidden;
    background: #d9b53c; transition: background-color .2s ease;
}
.gm-vcard-add .bws-card-create-btn {
    font-weight: 500; pointer-events: none;
    background: #d9b53c; border-color: #d9b53c;
    transition: background-color .2s ease, border-color .2s ease;
}
.gm-vcard-add:hover .bws-card-cover--custom,
.gm-vcard-add:hover .bws-card-create-btn {
    background: var(--color-accent, #ffd84b); border-color: var(--color-accent, #ffd84b);
}
.gm-vcard-add:focus-visible { outline: 2px solid var(--color-accent, #ffd84b); outline-offset: 2px; }

/* ── The add-a-voice dialog ─────────────────────────────────────────────── */
/* The frame, the glass and the padding come from .mlib-enhance-panel, the same
   as the Creator's. What is left here is the ✕: it is pinned to the corner,
   over the flow, so the heading and the paragraph under it have to keep their
   own hands off that square or the first line runs beneath it. */
.gm-voice-dialog { position: relative; }
.gm-voice-dialog h3 { margin: 0 0 6px; padding-right: 32px; }
.gm-vc-moderow { margin: 14px 0 12px; }

.gm-vc-filerow,
.gm-vc-recrow {
    display: flex; flex-wrap: wrap; align-items: center; gap: 10px;
}
/* The picked file's name, beside the button that picked it. Long names are cut
   rather than allowed to push the dialog wider. */
.gm-vc-filename {
    flex: 1 1 160px; min-width: 0; font-size: 12px; color: var(--text-muted, #888);
    overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}

/* Recording is the one destructive-feeling control in here — it turns the
   microphone on — so it says so in red while it runs. */
.gm-vc-recbtn.gm-vc-recbtn-on {
    color: #fff; background: #d0342c; border-color: #d0342c;
}
.gm-vc-rectime {
    font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
    font-size: 15px; font-variant-numeric: tabular-nums;
    color: var(--color-accent, #ffd84b);
}

.gm-vc-audio { width: 100%; margin: 4px 0 2px; }
.gm-vc-meta { font-size: 12px; color: var(--text-muted, #888); margin-bottom: 4px; }
.gm-vc-name { width: 100%; box-sizing: border-box; }

.gm-vc-status { margin-top: 10px; font-size: 13px; line-height: 1.5; color: var(--text-muted, #888); min-height: 1.2em; }
.gm-vc-status-error   { color: #ff9a8b; }
.gm-vc-status-warn    { color: #ffc46b; }
.gm-vc-status-loading { color: var(--color-accent, #ffd84b); }

.gm-vc-actions {
    display: flex; justify-content: flex-end; gap: 10px; margin-top: 14px; flex-wrap: wrap;
}
.gm-vc-save:disabled { opacity: .5; cursor: default; }

@media (max-width: 640px) {
    .gm-vc-actions { justify-content: stretch; }
    .gm-vc-actions > * { flex: 1 1 140px; }
}

/* The voice info modal: the preview sits in the body, with the format and
   length beside it — the two facts a reference recording is judged on. */
.gm-vi-playrow {
    display: flex; align-items: center; gap: 10px; flex-wrap: wrap; margin-top: 10px;
}
.gm-vi-format {
    font-size: 11px; letter-spacing: .06em; color: var(--text-muted, #888);
    font-variant-numeric: tabular-nums;
}
/* A voice has no artwork of its own until someone makes some, so the glyph
   stands in at modal size too. */
.alb-modal-cover.gm-vcard-cover .gm-vcard-glyph { max-width: 88px; }

/* The edit dialog is the shared save-dialog shell; only the picked-file line
   under the cover block needs anything of its own. */
.gm-voice-edit .gm-ed-uploadrow {
    display: flex; flex-wrap: wrap; align-items: center; gap: 10px; margin-top: 10px;
}
.gm-voice-edit .gm-ed-uploadrow .bws-group-description {
    flex: 1 1 180px; min-width: 0; margin: 0; font-size: 10px; line-height: 1.35; opacity: .8;
}
.gm-voice-edit .gm-ed-filename {
    flex: 1 1 100%; min-width: 0; font-size: 12px; color: var(--text-muted, #888);
    overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}


/* ── Tinnitus notch indicator (js/bws-notch-indicator.js) ─────────────────
   Shown in the app header for as long as the app-wide notch is on. Warning red
   with an exclamation mark: the setting is deliberate, but every module the
   person plays is being filtered because of it, and the one thing this has to
   beat is being forgotten about. */
.bws-notch-chip {
    display: none;                 /* → inline-flex once the notch is on */
    align-items: center;
    gap: 0.35rem;
    font-size: 12px;
    font-weight: 500;
    font-variant-numeric: tabular-nums;
    line-height: 1;
    padding: 0.25rem 0.55rem;
    background: var(--color-danger, #f44336);
    border: 1px solid var(--color-danger, #f44336);
    border-radius: 999px;
    color: #ffffff;
    cursor: pointer;
    white-space: nowrap;
    transition: background 0.15s, border-color 0.15s;
}
.bws-notch-chip.is-visible { display: inline-flex; }
.bws-notch-chip:hover,
.bws-notch-chip[aria-expanded="true"] {
    background: #d32f2f;
    border-color: #d32f2f;
}
/* The warning triangle. Inline-flex so the glyph sits on the text's centre line
   rather than its baseline. Its own colours rather than currentColor, which on
   this chip is the white label. */
.bws-notch-mark {
    display: inline-flex;
    align-items: center;
    animation: bwsNotchPulse 2.4s ease-in-out infinite;
}
.bws-notch-mark .bws-notch-tri  { fill: #ffffff; stroke: var(--color-danger, #f44336); }
.bws-notch-mark .bws-notch-bang { fill: none;    stroke: var(--color-danger, #f44336); }
@keyframes bwsNotchPulse {
    0%, 100% { opacity: 1; }
    50%      { opacity: 0.25; }
}
@media (prefers-reduced-motion: reduce) {
    .bws-notch-mark { animation: none; }
}
/* ── Borrowed marks (BWSNotchIndicator.attachMark) ─────────────────────
   The same triangle put beside something the notch is about to affect — the
   record button, the exporter's Export, the Now Playing header. The sign on its
   own, no plate: it is a warning to be read, not a control to be reached for.

   Centring it on the button it warns about cannot be done with `align-self`:
   these console rows are full of taller label/select groups, so the flex line is
   taller than the button and `center` would float the mark above it. Instead the
   mark bottom-aligns with the button — as everything in those rows does — and
   attachMark() gives it the button's own height, which puts the glyph on the
   button's centre line. */
.bws-notch-mark-btn {
    display: none;                 /* → inline-flex while the notch is on */
    align-items: center;
    justify-content: center;
    align-self: flex-end;
    flex: 0 0 auto;
    padding: 2px;
    border: 0;
    border-radius: 6px;
    background: none;
    color: var(--color-danger, #f44336);
    cursor: pointer;
    transition: background 0.15s;
}
.bws-notch-mark-btn.is-visible { display: inline-flex; }
.bws-notch-mark-btn:hover,
.bws-notch-mark-btn[aria-expanded="true"] { background: rgba(244, 67, 54, 0.18); }
.bws-notch-mark-btn svg { display: block; width: 17px; height: 17px; }
.bws-notch-mark-btn .bws-notch-tri  { fill: #ffffff; stroke: var(--color-danger, #f44336); }
.bws-notch-mark-btn .bws-notch-bang { fill: none;    stroke: var(--color-danger, #f44336); }

/* The sign on its own, with nothing to press — for labelling a thing as being
   about the notch rather than reporting that the notch is on. */
.bws-notch-sign {
    display: inline-flex;
    align-items: center;
    vertical-align: -3px;
}
.bws-notch-sign svg { display: block; width: 15px; height: 15px; }
.bws-notch-sign .bws-notch-tri  { fill: #ffffff; stroke: var(--color-danger, #f44336); }
.bws-notch-sign .bws-notch-bang { fill: none;    stroke: var(--color-danger, #f44336); }

/* A module opened on its own page has no header to sit in, so the chip takes
   the same corner on its own. */
.bws-notch-chip-float {
    position: fixed;
    top: 10px;
    right: 12px;
    z-index: 1150;
}

/* The panel: what is being filtered, and the way out. Positioned in JS from
   whichever mark opened it. The z-index clears the Now Playing panel (99990),
   whose header carries the second mark — 1200 was enough for the app header but
   would have opened this behind that panel. */
.bws-notch-pop {
    position: fixed;
    z-index: 99995;
    width: 264px;
    padding: 0.7rem 0.75rem 0.75rem;
    background: rgba(24, 24, 24, 0.98);
    border: 1px solid rgba(244, 67, 54, 0.35);
    border-radius: 8px;
    box-shadow: 0 10px 28px rgba(0, 0, 0, 0.5);
    backdrop-filter: blur(8px);
}
.bws-notch-pop-head {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: 1rem;
    padding-bottom: 0.5rem;
    margin-bottom: 0.5rem;
    border-bottom: 1px solid rgba(255, 255, 255, 0.08);
}
.bws-notch-pop-title {
    font-size: 11px;
    letter-spacing: 0.06em;
    text-transform: uppercase;
    color: rgba(255, 255, 255, 0.5);
}
.bws-notch-pop-state {
    font-size: 11px;
    font-weight: 700;
    letter-spacing: 0.04em;
    text-transform: uppercase;
    color: var(--color-danger, #f44336);
}
.bws-notch-row {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: 0.75rem;
    padding: 0.2rem 0;
    font-size: 12px;
    color: rgba(255, 255, 255, 0.5);
}
.bws-notch-row b {
    font-weight: 600;
    font-variant-numeric: tabular-nums;
    text-align: right;
    color: rgba(255, 255, 255, 0.9);
}
.bws-notch-row-dim b { font-weight: 400; color: rgba(255, 255, 255, 0.55); }
.bws-notch-pop-note {
    margin: 0.55rem 0 0;
    font-size: 11px;
    line-height: 1.5;
    color: rgba(255, 255, 255, 0.4);
}
/* Colour, border and hover come from .bws-button-a.playing — the same button the
   Tinnitus module's "Notch Every Module" card uses to switch this off. Only the
   fit is ours, and it matches .tin-notch-toggle there. */
.bws-notch-pop-off {
    width: 100%;
    min-width: 0;
    margin-top: 0.7rem;
    padding: 0.4rem 0.8rem;
    font-size: 12px;
}

/* ── Keyboard control indicator (js/bws-keyboard-control.js) ──────────────
   Shown in the app header for as long as one module owns the keyboard. Not a
   warning like the notch chip — nothing is being degraded, a control has simply
   moved somewhere the person may have scrolled past — but it wears the notch
   chip's red plate and white label all the same: these two are the app's only
   header chips, they report the same kind of thing (something switched on
   elsewhere is acting on you here), and giving them one look makes that a
   category the person learns once. It sits still rather than pulsing. */
.bws-kbctl-chip {
    display: none;                 /* → inline-flex once a module holds it */
    align-items: center;
    gap: 0.3rem;
    font-size: 12px;
    font-weight: 500;
    line-height: 1;
    padding: 0.25rem 0.3rem 0.25rem 0.55rem;
    background: var(--color-danger, #f44336);
    border: 1px solid var(--color-danger, #f44336);
    border-radius: 999px;
    color: #ffffff;
    white-space: nowrap;
}
.bws-kbctl-chip.is-visible { display: inline-flex; }

.bws-kbctl-mark { display: inline-flex; align-items: center; }
.bws-kbctl-mark svg { display: block; }

/* The label doubles as the way back to the module that holds the keyboard, so
   it is a button — but a bare one: on a chip this size a second plate inside the
   plate would read as two controls rather than one. */
.bws-kbctl-label {
    padding: 0;
    border: 0;
    background: none;
    font: inherit;
    color: inherit;
    cursor: pointer;
    text-decoration: underline;
    text-decoration-color: transparent;
    text-underline-offset: 2px;
    transition: text-decoration-color 0.15s;
}
.bws-kbctl-label:hover { text-decoration-color: currentColor; }

/* The way out. Dimmed until pointed at, so the chip reads as one statement with
   a cross on the end rather than as a button someone has to decide about. */
.bws-kbctl-off {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    padding: 2px;
    border: 0;
    border-radius: 999px;
    background: none;
    color: inherit;
    opacity: 0.55;
    cursor: pointer;
    transition: opacity 0.15s, background 0.15s;
}
.bws-kbctl-off:hover { opacity: 1; background: rgba(255, 255, 255, 0.22); }
.bws-kbctl-off svg { display: block; }

/* The same glyph on the modules' own Keyboard Control buttons. Here it takes
   the label's colour rather than the chip's white — on those buttons it is part
   of the label, not a mark on a plate — which the SVG's currentColor strokes do
   on their own. The buttons keep their own sizing, so this only has to sit the
   glyph on the text's centre line and leave a gap after it. */
.bws-kbctl-btn-icon {
    display: inline-flex;
    align-items: center;
    vertical-align: -3px;
    margin-right: 0.4rem;
    color: inherit;
}
.bws-kbctl-btn-icon svg { display: block; }

/* A module opened on its own page has no header to sit in, so the chip takes
   the same corner on its own. Offset down from the notch chip's corner, which
   both can occupy at once. */
.bws-kbctl-chip-float {
    position: fixed;
    top: 44px;
    right: 12px;
    z-index: 1150;
}


/* ════════════════════════════════════════════════════════════════════════════
   AI buttons — one look for anything that spends credits
   ════════════════════════════════════════════════════════════════════════════
   Every button in the app that bills the AI wallet wears the same violet and
   the same lightning bolt, whichever module it lives in. The colour is the one
   the header's credit chip and the library's Enhance button already used, so
   the button that spends and the balance it spends from read as one system;
   gold stays what it always was, the site's own accent for everything free.

   One resting state everywhere, the credit pill's: a dark translucent violet
   behind a lighter violet line. The button that spends and the balance it
   spends from are then literally the same object in two places.

   Two weights, differing in SIZE and not in colour:
     • PRIMARY   — the action a dialog exists to run (Create Meditation, Build,
       Generate cover image). Roomier, heavier label, and it brightens further
       under the cursor.
     • SECONDARY — an AI action offered alongside free ones in a busy row (AI
       auto-adjust pauses, Enhance).

   Placed at the end of the sheet on purpose: every rule below restates a
   selector that is already styled elsewhere in this file, at equal specificity,
   so ORDER is what carries the repaint. Adding a rule for one of these buttons
   after this block will quietly undo it. */

/* The palette, in one place. */
:root {
    --ai-violet:        #9370db;
    /* Rest is the solid-bordered state: these buttons are meant to be found, and
       a translucent line at rest read as disabled next to the accent ones. */
    --ai-violet-fill:   rgba(147, 112, 219, 0.30);
    --ai-violet-line:   #9370db;
    --ai-violet-ink:    #d8cbf5;
    /* Hover only lifts: a little more fill, a lighter line, and a glow small
       enough to be felt rather than seen. */
    --ai-violet-fill-h: rgba(147, 112, 219, 0.42);
    --ai-violet-line-h: #b39ddb;
    --ai-violet-glow:   0 0 10px rgba(147, 112, 219, 0.45);
}

/* ── The button ──────────────────────────────────────────────────────────── */
/* One size and one colour for all of them. There is no primary/secondary split
   any more: these buttons are already set apart from everything around them by
   being violet, and a second rank inside that was a distinction without a job.

   The geometry is .bws-button-a's — the site's gold primary — so an AI button
   and an ordinary one standing side by side are the same height and weight, and
   differ only in what the colour says about them.

   The classes are doubled on the resting rules on purpose. These buttons carry
   their module's own classes too, and any two-class rule among the 15k lines
   above outranks a single-class one whatever the order — which is how a button
   could end up violet on hover (where :hover:not() lends the extra weight) and
   its own old colour at rest. */
.bws-ai-btn.bws-ai-btn,
.bws-ai-btn-primary.bws-ai-btn-primary,
.gmx-btn.gmx-aipause,
.bws-ai-builder .prb-ai-btn,
.bws-ai-builder .prb-chat-buildnow,
.bws-cover-dialog .prb-cover-gen {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: 7px;
    padding: .75rem 2rem;
    border: 1px solid var(--ai-violet-line);
    border-radius: 4px;
    background: var(--ai-violet-fill);
    color: var(--ai-violet-ink);
    font-size: 13px;
    font-weight: 400;
    white-space: nowrap;
    transition: background-color .2s ease, border-color .2s ease, box-shadow .2s ease;
}
.bws-ai-btn:hover:not(:disabled),
.bws-ai-btn-primary:hover:not(:disabled),
.gmx-btn.gmx-aipause:hover:not(:disabled),
.bws-ai-builder .prb-ai-btn:hover:not(:disabled),
.bws-ai-builder .prb-chat-buildnow:hover:not(:disabled),
.bws-cover-dialog .prb-cover-gen:hover:not(:disabled) {
    background: var(--ai-violet-fill-h);
    border-color: var(--ai-violet-line-h);
    color: #fff;
    box-shadow: var(--ai-violet-glow);
}
/* Note what is NOT in these lists: .mlib-generate-btn. It is a LAYOUT class, and
   the media library hangs it on the recovery scan too — a free read of jobs that
   already ran. A button is dressed as an AI button because it spends, never
   because it sits in the row where spending usually happens. */

.bws-ai-btn:disabled,
.bws-ai-btn-primary:disabled,
.gmx-btn.gmx-aipause:disabled,
.bws-ai-builder .prb-ai-btn:disabled,
.bws-ai-builder .prb-chat-buildnow:disabled,
.bws-cover-dialog .prb-cover-gen:disabled { opacity: 0.55; cursor: default; }

/* ── The bolt ────────────────────────────────────────────────────────────── */
/* A masked shape rather than inline SVG, so a button opts in with one empty
   element and no module has to carry a copy of the path. It paints in
   currentColor, so it inverts and dims with the label it sits beside. */
.bws-ai-ico {
    flex: none;
    display: block;
    width: 15px;
    height: 15px;
    background: currentColor;
    -webkit-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath d='M13 2 4.5 13.5H11l-1 8.5 8.5-11.5H12z'/%3E%3C/svg%3E") center / contain no-repeat;
    mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath d='M13 2 4.5 13.5H11l-1 8.5 8.5-11.5H12z'/%3E%3C/svg%3E") center / contain no-repeat;
}
/* Buttons that carry no icon of their own get one automatically. :has() is what
   keeps the two from doubling up on a button that spells the icon out. */
.bws-ai-btn:not(:has(.bws-ai-ico))::before,
.bws-ai-btn-primary:not(:has(.bws-ai-ico))::before,
.bws-ai-builder .prb-ai-btn:not(:has(.bws-ai-ico)):not(:has(.prb-btn-icon))::before,
.bws-ai-builder .prb-chat-buildnow:not(:has(.bws-ai-ico))::before,
.bws-cover-dialog .prb-cover-gen:not(:has(.bws-ai-ico))::before {
    content: '';
    flex: none;
    width: 15px;
    height: 15px;
    background: currentColor;
    -webkit-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath d='M13 2 4.5 13.5H11l-1 8.5 8.5-11.5H12z'/%3E%3C/svg%3E") center / contain no-repeat;
    mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath d='M13 2 4.5 13.5H11l-1 8.5 8.5-11.5H12z'/%3E%3C/svg%3E") center / contain no-repeat;
}

/* ── The price ───────────────────────────────────────────────────────────── */
/* Sits in front of the bolt and reads as one unit with it, so the number needs
   no "credits" after it. Tight against the icon, apart from the label. */
.bws-ai-cost {
    flex: none;
    margin-right: -5px;
    font-weight: 700;
    font-variant-numeric: tabular-nums;
}

/* The working pulse takes the button's own colour: a gold halo around a violet
   button is the one place the two palettes would have met. Scoped to the AI
   dress rather than to .mlib-btn-generating, which the free recovery scan also
   wears. */
@keyframes bws-ai-pulse {
    0%, 100% { box-shadow: 0 0 6px rgba(147, 112, 219, 0.45); }
    50%      { box-shadow: 0 0 18px rgba(147, 112, 219, 0.85); }
}
.bws-ai-btn-primary.mlib-btn-generating,
.bws-ai-btn.mlib-btn-generating { animation: bws-ai-pulse 1.4s ease-in-out infinite; }

