*{box-sizing:border-box;margin:0;padding:0}

/* ── LIGHT MODE tokens ─────────────────────────────────────── */
[data-mode="light"]{
  --bg:                 #e7e5f2;
  --bg-glow-a:          rgba(120,115,200,.2);
  --bg-glow-b:          rgba(90,85,170,.16);
  --bg-glow-c:          rgba(150,145,220,.1);
  --nav-bg:             rgba(244,243,250,.75);
  --nav-border:         rgba(41,39,64,.1);
  --text-primary:       #292740;
  --text-secondary:     rgba(41,39,64,.55);
  --text-dim:           rgba(41,39,64,.35);
  --text-faint:         rgba(41,39,64,.28);
  --accent:             #3d3a8c;
  --accent-deep:        #2f2c6b;
  --accent-bg:          rgba(61,58,140,.1);
  --accent-border:      rgba(61,58,140,.22);
  --accent-badge-bg:    rgba(61,58,140,.12);
  --accent-badge-text:  #2f2c6b;
  --pill-bg:            rgba(41,39,64,.06);
  --pill-border:        rgba(41,39,64,.1);
  --pill-text:          rgba(41,39,64,.45);
  --pill-on-bg:         rgba(41,39,64,.12);
  --pill-on-border:     rgba(41,39,64,.28);
  --pill-on-text:       #292740;
  --card-bg:            rgba(250,249,253,.55);
  --card-border:        rgba(255,255,255,.65);
  --card-hover-border:  rgba(61,58,140,.3);
  --card-hover-shadow:  rgba(35,33,60,.12);
  --card-shadow:        rgba(35,33,60,.06);
  --card-sunday-bg:     rgba(245,244,252,.6);
  --card-sunday-border: rgba(61,58,140,.28);
  --icon-bg:            rgba(61,58,140,.1);
  --icon-border:        rgba(61,58,140,.15);
  --icon-text:          #3d3a8c;
  --chip-bg:            rgba(41,39,64,.04);
  --chip-border:        rgba(41,39,64,.08);
  --chip-hover-bg:      rgba(61,58,140,.08);
  --chip-hover-border:  rgba(61,58,140,.2);
  --chip-date:          rgba(41,39,64,.35);
  --chip-stems:         rgba(41,39,64,.3);
  --btn-bg:             rgba(41,39,64,.06);
  --btn-border:         rgba(41,39,64,.1);
  --btn-text:           rgba(41,39,64,.55);
  --btn-hover-bg:       rgba(41,39,64,.1);
  --btn-hover-border:   rgba(41,39,64,.22);
  --btn-hover-text:     #292740;
  --btn-sun-bg:         rgba(61,58,140,.1);
  --btn-sun-border:     rgba(61,58,140,.2);
  --btn-sun-text:       #2f2c6b;
  --btn-sun-hover:      rgba(61,58,140,.18);
  --link-text:          rgba(41,39,64,.42);
  --link-hover-bg:      rgba(41,39,64,.07);
  --link-hover-text:    rgba(41,39,64,.85);
  --link-me-bg:         rgba(41,39,64,.08);
  --link-me-text:       rgba(41,39,64,.7);
  --ping-color:         #3d3a8c;
  --bar-bg:             rgba(61,58,140,.1);
  --bar-border:         rgba(61,58,140,.25);
  --bar-text:           rgba(41,39,64,.6);
  --bar-strong:         #2f2c6b;
  --divider:            rgba(41,39,64,.08);
  /* Studio "console section" identity colors (PHASE 4d) — EQ reuses --accent
     (it already colors the curve/dots, so the card's label just confirms
     that association) — Processing gets its own hue so the two cards read
     as two distinct labeled console sections, not one continuous surface. */
  --section-fx:         #2f7ea0;
  --section-fx-bg:      rgba(47,126,160,.1);
  /* ── extended: shared components ── */
  --strip-bg:           rgba(250,249,253,.5);
  --strip-border:       rgba(41,39,64,.1);
  --strip-inner:        rgba(41,39,64,.03);
  --fader-track:        rgba(41,39,64,.12);
  --fader-fill:         rgba(61,58,140,.5);
  --fader-thumb:        #3d3a8c;
  --fader-thumb-br:     rgba(255,255,255,.85);
  --vu-off:             rgba(41,39,64,.1);
  --vu-green:           #7fa05a;
  --vu-amber:           #c79a44;
  --vu-red:             #c2604a;
  --ctrl-bg:            rgba(41,39,64,.05);
  --ctrl-border:        rgba(41,39,64,.12);
  --ctrl-text:          rgba(41,39,64,.6);
  --mute-on-bg:         rgba(194,96,74,.16);
  --mute-on-br:         rgba(194,96,74,.4);
  --mute-on-text:       #b14a32;
  --solo-on-bg:         rgba(199,154,68,.18);
  --solo-on-br:         rgba(199,154,68,.45);
  --solo-on-text:       #8a6420;
  --scrub-track:        rgba(41,39,64,.12);
  --scrub-fill:         #3d3a8c;
  --scrub-handle:       #2f2c6b;
  --input-bg:           rgba(250,249,253,.7);
  --input-border:       rgba(41,39,64,.18);
  --input-text:         #292740;
  --input-focus:        rgba(61,58,140,.55);
  --overlay:            rgba(30,28,50,.38);
  --modal-bg:           #f5f4fa;
  --modal-border:       rgba(255,255,255,.7);
  --danger:             #b14a32;
  --danger-bg:          rgba(194,96,74,.12);
  --danger-border:      rgba(194,96,74,.35);
  --danger-hover:       rgba(194,96,74,.2);
  --role-bg:            rgba(61,58,140,.12);
  --role-text:          #2f2c6b;
  --owner-bg:           rgba(61,58,140,.2);
  --owner-text:         #26235c;
  --table-border:       rgba(41,39,64,.08);
  --table-head:         rgba(41,39,64,.45);
  --row-hover:          rgba(61,58,140,.05);
  --toast-bg:           rgba(30,28,50,.93);
  --toast-text:         #f5f4fa;
  --menu-bg:            #f6f5fb;
  --menu-border:        rgba(41,39,64,.12);
  --menu-shadow:        rgba(30,28,55,.18);
  --lim-on:             #c2604a;
  --primary-bg:         #3d3a8c;
  --primary-text:       #f7f6fc;
  --primary-hover:      #332f78;
  /* Planning Center's actual verified brand colors (Services). True
     neutrals, deliberately NOT the app's own purple-tinted greys — PCO
     elements should read as Planning Center, not blend into Sanctuary's
     own design language. */
  /* Deeper, less acidic than Planning Center's own #54B84D brand green, which
     read as too bright against the Indigo Hour palette. Kept unmistakably in
     the PCO green family so the integration still reads as "theirs" — just
     dialled down. Side benefit: .sunday-badge.you puts white text directly on
     this colour, and #54B84D behind white was only about 2.4:1 contrast (well
     under the 4.5:1 small-text threshold); this is ~5.2:1. All four values
     move together — every PCO surface in the app derives from these, so this
     block is the only place to adjust if you want it darker or lighter still. */
  --pco-green:          #38853C;
  --pco-green-hover:    #2C6B30;
  --pco-green-bg:       rgba(56,133,60,.12);
  --pco-green-border:   rgba(56,133,60,.32);
  --pco-charcoal:       #333333;
  --pco-grey:           #888888;
  --pco-grey-bg:        rgba(136,136,136,.12);
  --pco-text:           #000000;
}

/* ── DARK MODE tokens ──────────────────────────────────────── */
[data-mode="dark"]{
  --bg:                 #14131f;
  --bg-glow-a:          rgba(110,100,200,.2);
  --bg-glow-b:          rgba(80,70,160,.16);
  --bg-glow-c:          rgba(70,60,140,.1);
  --nav-bg:             rgba(20,19,31,.8);
  --nav-border:         rgba(205,200,235,.08);
  --text-primary:       #eeecf7;
  --text-secondary:     rgba(238,236,247,.55);
  --text-dim:           rgba(238,236,247,.35);
  --text-faint:         rgba(238,236,247,.25);
  --accent:             #8b85e0;
  --accent-deep:        #a49ee8;
  --accent-bg:          rgba(139,133,224,.08);
  --accent-border:      rgba(139,133,224,.2);
  --accent-badge-bg:    rgba(139,133,224,.12);
  --accent-badge-text:  #a49ee8;
  --pill-bg:            rgba(205,200,235,.06);
  --pill-border:        rgba(205,200,235,.1);
  --pill-text:          rgba(238,236,247,.42);
  --pill-on-bg:         rgba(205,200,235,.14);
  --pill-on-border:     rgba(205,200,235,.28);
  --pill-on-text:       #eeecf7;
  --card-bg:            rgba(220,217,240,.05);
  --card-border:        rgba(205,200,235,.1);
  --card-hover-border:  rgba(205,200,235,.22);
  --card-hover-shadow:  rgba(0,0,0,.3);
  --card-shadow:        rgba(0,0,0,.15);
  --card-sunday-bg:     rgba(139,133,224,.07);
  --card-sunday-border: rgba(139,133,224,.25);
  --icon-bg:            rgba(205,200,235,.08);
  --icon-border:        rgba(205,200,235,.14);
  --icon-text:          rgba(139,133,224,.85);
  --chip-bg:            rgba(205,200,235,.05);
  --chip-border:        rgba(205,200,235,.08);
  --chip-hover-bg:      rgba(205,200,235,.11);
  --chip-hover-border:  rgba(205,200,235,.2);
  --chip-date:          rgba(238,236,247,.3);
  --chip-stems:         rgba(238,236,247,.25);
  --btn-bg:             rgba(205,200,235,.07);
  --btn-border:         rgba(205,200,235,.11);
  --btn-text:           rgba(238,236,247,.5);
  --btn-hover-bg:       rgba(205,200,235,.13);
  --btn-hover-border:   rgba(205,200,235,.22);
  --btn-hover-text:     #eeecf7;
  --btn-sun-bg:         rgba(139,133,224,.12);
  --btn-sun-border:     rgba(139,133,224,.25);
  --btn-sun-text:       #a49ee8;
  --btn-sun-hover:      rgba(139,133,224,.2);
  --link-text:          rgba(238,236,247,.38);
  --link-hover-bg:      rgba(205,200,235,.08);
  --link-hover-text:    rgba(238,236,247,.85);
  --link-me-bg:         rgba(205,200,235,.1);
  --link-me-text:       rgba(238,236,247,.7);
  --ping-color:         #8b85e0;
  --bar-bg:             rgba(139,133,224,.08);
  --bar-border:         rgba(139,133,224,.2);
  --bar-text:           rgba(238,236,247,.58);
  --bar-strong:         #a49ee8;
  --divider:            rgba(205,200,235,.07);
  --link-border:        rgba(205,200,235,.14);
  --section-fx:         #6ab8d9;
  --section-fx-bg:      rgba(106,184,217,.12);
  /* ── extended: shared components ── */
  --strip-bg:           rgba(220,217,240,.04);
  --strip-border:       rgba(205,200,235,.1);
  --strip-inner:        rgba(205,200,235,.03);
  --fader-track:        rgba(205,200,235,.12);
  --fader-fill:         rgba(139,133,224,.45);
  --fader-thumb:        #8b85e0;
  --fader-thumb-br:     rgba(20,19,31,.6);
  --vu-off:             rgba(205,200,235,.08);
  --vu-green:           #8fb061;
  --vu-amber:           #d4ad52;
  --vu-red:             #d97a5f;
  --ctrl-bg:            rgba(205,200,235,.06);
  --ctrl-border:        rgba(205,200,235,.12);
  --ctrl-text:          rgba(238,236,247,.55);
  --mute-on-bg:         rgba(217,122,95,.18);
  --mute-on-br:         rgba(217,122,95,.4);
  --mute-on-text:       #e89177;
  --solo-on-bg:         rgba(212,173,82,.2);
  --solo-on-br:         rgba(212,173,82,.45);
  --solo-on-text:       #e0c06f;
  --scrub-track:        rgba(205,200,235,.14);
  --scrub-fill:         #8b85e0;
  --scrub-handle:       #a49ee8;
  --input-bg:           rgba(205,200,235,.06);
  --input-border:       rgba(205,200,235,.16);
  --input-text:         #eeecf7;
  --input-focus:        rgba(139,133,224,.5);
  --overlay:            rgba(0,0,0,.62);
  --modal-bg:           #1d1c2c;
  --modal-border:       rgba(205,200,235,.12);
  --danger:             #e89177;
  --danger-bg:          rgba(217,122,95,.14);
  --danger-border:      rgba(217,122,95,.35);
  --danger-hover:       rgba(217,122,95,.22);
  --role-bg:            rgba(139,133,224,.14);
  --role-text:          #a49ee8;
  --owner-bg:           rgba(139,133,224,.22);
  --owner-text:         #b8b2ec;
  --table-border:       rgba(205,200,235,.08);
  --table-head:         rgba(238,236,247,.4);
  --row-hover:          rgba(205,200,235,.05);
  --toast-bg:           rgba(240,238,248,.96);
  --toast-text:         #14131f;
  --menu-bg:            #201f30;
  --menu-border:        rgba(205,200,235,.12);
  --menu-shadow:        rgba(0,0,0,.45);
  --lim-on:             #d97a5f;
  --primary-bg:         #8b85e0;
  --primary-text:       #14131f;
  --primary-hover:      #a49ee8;
  /* Dark mode can't simply reuse the light-mode value — a deep green that
     reads well on a pale background starts disappearing into a near-black one.
     This is darker than the previous #5CA058 but deliberately not as dark as
     the light-mode token, which keeps it legible for the folder labels and
     step dots that sit directly on the page background. */
  --pco-green:          #4A8D4E;
  --pco-green-hover:    #589C5C;
  --pco-green-bg:       rgba(74,141,78,.16);
  --pco-green-border:   rgba(74,141,78,.34);
  --pco-charcoal:       #e6e6e6;
  --pco-grey:           #9a9a9a;
  --pco-grey-bg:        rgba(154,154,154,.14);
  --pco-text:           #f5f5f5;
}

/* ── BASE ─────────────────────────────────────────────────── */
body{
  background:var(--bg);
  color:var(--text-primary);
  font-family:'Inter',sans-serif;
  font-size:14px;
  min-height:100vh;
  transition:background .4s, color .3s;
}
button{font-family:inherit}
:focus-visible{outline:2px solid var(--accent);outline-offset:2px;border-radius:4px}

/* ── NAV ──────────────────────────────────────────────────── */
nav{
  display:flex;align-items:center;justify-content:space-between;
  padding:20px 40px;
  background:var(--nav-bg);
  backdrop-filter:blur(18px);-webkit-backdrop-filter:blur(18px);
  border-bottom:1px solid var(--nav-border);
  position:sticky;top:0;z-index:50;
  transition:background .4s, border-color .3s;
}
.logo{display:flex;align-items:center;gap:9px;font-family:'Cormorant Garamond',serif;font-size:20px;font-weight:600;color:var(--text-primary);letter-spacing:.01em;transition:color .3s;cursor:pointer;background:none;border:none}
.logo-mark{flex:0 0 auto;display:block}
.logo em{font-style:italic;color:var(--accent)}
.nav-right{display:flex;align-items:center;gap:6px}
.nav-right a,.nav-link{font-size:13px;color:var(--link-text);text-decoration:none;padding:7px 14px;border-radius:20px;transition:background .15s,color .15s;cursor:pointer;background:none;border:none;font-family:inherit}
.nav-right a:hover,.nav-link:hover{background:var(--link-hover-bg);color:var(--link-hover-text)}
.nav-right a.active,.nav-link.active{background:var(--link-me-bg);color:var(--link-me-text)}
.nav-me{position:relative}
.me-btn{font-size:13px;color:var(--link-me-text);background:var(--link-me-bg);border:none;padding:7px 14px;border-radius:20px;cursor:pointer;font-family:inherit;display:flex;align-items:center;gap:6px;transition:background .15s}
.me-btn:hover{background:var(--link-hover-bg)}
.me-btn .caret{font-size:9px;opacity:.6}

/* ── USER MENU ────────────────────────────────────────────── */
.menu{
  position:absolute;top:calc(100% + 8px);right:0;min-width:230px;z-index:120;
  background:var(--menu-bg);border:1px solid var(--menu-border);border-radius:16px;
  box-shadow:0 16px 44px var(--menu-shadow);overflow:hidden;
  padding:6px;
}
.menu-head{padding:12px 12px 10px;border-bottom:1px solid var(--divider);margin-bottom:6px}
.menu-name{font-family:'Cormorant Garamond',serif;font-size:19px;font-weight:600;color:var(--text-primary);line-height:1.1}
.menu-sub{font-size:11px;color:var(--text-dim);margin-top:2px}
.menu-item{display:flex;align-items:center;gap:9px;width:100%;text-align:left;background:none;border:none;font-family:inherit;font-size:13px;color:var(--text-secondary);padding:9px 11px;border-radius:10px;cursor:pointer;transition:background .12s,color .12s}
.menu-item:hover{background:var(--link-hover-bg);color:var(--text-primary)}
.menu-item.danger{color:var(--danger)}
.menu-item.danger:hover{background:var(--danger-bg)}
.menu-item.disabled{color:var(--text-faint);cursor:default}
.menu-item.disabled:hover{background:none;color:var(--text-faint)}
.menu-note{padding:7px 11px 9px;font-size:11px;color:var(--text-faint);line-height:1.4}

/* ── MODE TOGGLE ──────────────────────────────────────────── */
.toggle-wrap{display:flex;align-items:center;gap:10px;margin-left:16px;padding-left:16px;border-left:1px solid var(--divider)}
.toggle-label{font-size:11px;color:var(--text-dim);letter-spacing:.04em;user-select:none;transition:color .3s}
.toggle{position:relative;width:46px;height:26px;flex-shrink:0}
.toggle input{opacity:0;width:0;height:0;position:absolute}
.toggle-track{
  position:absolute;inset:0;border-radius:999px;cursor:pointer;
  background:var(--pill-bg);border:1px solid var(--pill-border);
  transition:background .3s,border-color .3s;
}
[data-mode="dark"] .toggle-track{background:rgba(139,133,224,.18);border-color:rgba(139,133,224,.3)}
.toggle-thumb{
  position:absolute;top:3px;left:3px;width:18px;height:18px;border-radius:50%;
  background:var(--accent);
  transition:transform .3s cubic-bezier(.34,1.4,.64,1), background .3s;
  display:flex;align-items:center;justify-content:center;font-size:10px;
}
[data-mode="dark"] .toggle-thumb{transform:translateX(20px)}
.toggle-icon{line-height:1;font-size:11px}

/* ── PAGE HEADER ──────────────────────────────────────────── */
.pg-head{padding:52px 40px 28px;display:flex;align-items:flex-end;justify-content:space-between;gap:20px}
.pg-head h1{font-family:'Cormorant Garamond',serif;font-size:52px;font-weight:400;color:var(--text-primary);line-height:1;transition:color .3s}
.pg-head h1 em{font-style:italic;color:var(--accent)}
.pg-meta{font-size:13px;color:var(--text-dim);white-space:nowrap}

/* ── PILLS ────────────────────────────────────────────────── */
.pills{display:flex;gap:6px;padding:0 40px 28px;flex-wrap:wrap}
.lib-toolbar{display:flex;align-items:center;gap:18px;padding:0 40px 28px;flex-wrap:wrap}
.lib-toolbar .pills{flex:1 1 320px;padding:0;min-width:0}
.lib-search{display:flex;align-items:center;gap:8px;flex:0 0 auto;background:var(--card-bg);border:1px solid var(--card-border);border-radius:999px;padding:7px 15px;box-shadow:0 2px 14px var(--card-shadow);backdrop-filter:blur(12px);-webkit-backdrop-filter:blur(12px);transition:border-color .15s,box-shadow .15s}
.lib-search:focus-within{border-color:var(--input-focus);box-shadow:0 2px 14px var(--card-shadow),0 0 0 3px var(--accent-bg)}
.lib-search svg{color:var(--text-faint);flex:0 0 auto}
.lib-search input::-webkit-search-cancel-button{-webkit-appearance:none;appearance:none;display:none}
.lib-search input{font-family:inherit;font-size:13px;color:var(--input-text);background:transparent;border:none;outline:none;width:188px;padding:2px 0;margin:0}
.lib-search input::placeholder{color:var(--text-faint)}
.size-ctrl{display:flex;align-items:center;gap:10px;flex:0 0 auto;height:34px;background:var(--card-bg);border:1px solid var(--card-border);border-radius:999px;padding:0 16px;box-shadow:0 2px 14px var(--card-shadow);backdrop-filter:blur(12px);-webkit-backdrop-filter:blur(12px)}
.size-ctrl .sz-lab{font-size:10px;letter-spacing:.08em;text-transform:uppercase;color:var(--text-dim);font-weight:600}
.size-ctrl .sz-ico{color:var(--text-faint);display:flex;align-items:center}
.size-ctrl input[type=range]{-webkit-appearance:none;appearance:none;width:130px;height:4px;border-radius:999px;background:var(--pill-on-bg);outline:none;cursor:pointer;margin:0}
.size-ctrl input[type=range]::-webkit-slider-thumb{-webkit-appearance:none;appearance:none;width:16px;height:16px;border-radius:50%;background:var(--accent);border:2px solid var(--bg);box-shadow:0 1px 4px rgba(0,0,0,.25);cursor:grab}
.size-ctrl input[type=range]::-webkit-slider-thumb:active{cursor:grabbing}
.size-ctrl input[type=range]::-moz-range-thumb{width:16px;height:16px;border-radius:50%;background:var(--accent);border:2px solid var(--bg);box-shadow:0 1px 4px rgba(0,0,0,.25);cursor:grab}
.size-ctrl input[type=range]::-moz-range-track{height:4px;border-radius:999px;background:var(--pill-on-bg)}
.pill{
  display:flex;align-items:center;justify-content:center;height:34px;
  background:var(--pill-bg);border:1px solid var(--pill-border);
  border-radius:999px;padding:0 16px;font-size:12px;font-weight:500;
  color:var(--pill-text);cursor:pointer;transition:all .15s;font-family:inherit;
}
/* The key filter pills are <div>s, but the Sync stems button is a real
   <button> sharing this same class — browsers (Firefox especially, via its
   invisible -moz-focus-inner padding on buttons) apply extra default sizing
   to <button> that a <div> never gets, even with identical declared padding.
   This neutralizes that so a button.pill renders at the exact same height as
   a div.pill. */
button.pill{-webkit-appearance:none;appearance:none;margin:0;line-height:1.2;box-sizing:border-box}
button.pill::-moz-focus-inner{border:0;padding:0}
.pill:hover{background:var(--pill-on-bg);color:var(--pill-on-text);border-color:var(--pill-on-border)}
.pill.on{background:var(--pill-on-bg);border-color:var(--pill-on-border);color:var(--pill-on-text)}

/* ── GRID + CARDS ─────────────────────────────────────────── */
.grid{display:grid;grid-template-columns:repeat(3,1fr);gap:12px;padding:0 40px 60px}
.card{border-radius:20px;cursor:pointer;transition:transform .2s}
.card:hover{transform:translateY(-4px)}
.card-glass{
  border-radius:20px;border:1px solid var(--card-border);background:var(--card-bg);
  backdrop-filter:blur(20px);-webkit-backdrop-filter:blur(20px);
  padding:24px;transition:all .25s;box-shadow:0 2px 24px var(--card-shadow);height:100%;
}
.card:hover .card-glass{border-color:var(--card-hover-border);box-shadow:0 8px 32px var(--card-hover-shadow)}
.card.sunday .card-glass{border-color:var(--card-sunday-border);background:var(--card-sunday-bg)}
.card-top{display:flex;align-items:flex-start;justify-content:space-between;margin-bottom:18px}
.card-icon{width:44px;height:44px;border-radius:14px;background:var(--icon-bg);border:1px solid var(--icon-border);display:flex;align-items:center;justify-content:center;transition:background .3s,border-color .3s}
.card-icon .letter{font-family:'Cormorant Garamond',serif;font-size:24px;font-style:italic;color:var(--icon-text);font-weight:600;transition:color .3s}
.sunday-badge{display:inline-flex;align-items:center;gap:5px;font-size:10px;letter-spacing:.1em;text-transform:uppercase;font-weight:600;color:var(--accent-badge-text);background:var(--accent-badge-bg);border:1px solid var(--accent-border);border-radius:999px;padding:3px 10px;transition:all .3s}
/* The little glowing dot — always present when the badge is, its color
   matching whichever variant is active. Sized and styled the same as the
   old toggle button's dot used to be when "on", just living in a plain
   badge now instead of a clickable control. */
.sb-dot{width:6px;height:6px;border-radius:50%;background:var(--accent);box-shadow:0 0 6px var(--accent);flex-shrink:0;transition:background .3s,box-shadow .3s}
/* PCO variant — only applied when the upcoming session actually came from
   a real Planning Center import (upcoming_from_pco), never for a
   manually created session that just happens to be dated soon. Same
   subtle-tint shape as the plain badge, just in Planning Center's own
   green rather than the app's indigo accent, so provenance is visible
   at a glance. */
.sunday-badge.pco{color:var(--pco-green);background:var(--pco-green-bg);border-color:var(--pco-green-border)}
.sunday-badge.pco .sb-dot{background:var(--pco-green);box-shadow:0 0 6px var(--pco-green)}
/* The personalized variant (im_scheduled) — a solid fill rather than the
   plain badge's subtle tint, on purpose: this is a stronger, more
   specific signal ("you, personally, are playing this") than the general
   badge everyone sees, and it should read as more important at a glance,
   not just differently colored. Green, not indigo — im_scheduled always
   means a real, confirmed-or-pending match against Planning Center's own
   roster, so this was never really an "accent" signal, it's a PCO one. */
.sunday-badge.you{color:#fff;background:var(--pco-green);border-color:var(--pco-green)}
.sunday-badge.you .sb-dot{background:#fff;box-shadow:0 0 6px rgba(255,255,255,.8)}
.card.mine .card-glass{border-color:var(--pco-green);box-shadow:0 0 0 1px var(--pco-green) inset}
.card-title{font-family:'Cormorant Garamond',serif;font-size:22px;font-weight:600;color:var(--text-primary);line-height:1.2;margin-bottom:4px;transition:color .3s}
.card-sub{font-size:12px;color:var(--text-dim);margin-bottom:18px;transition:color .3s}
.sess-row{display:flex;gap:7px;margin-bottom:16px;flex-wrap:wrap}
.sess-chip{flex:1;min-width:84px;background:var(--chip-bg);border:1px solid var(--chip-border);border-radius:12px;padding:10px 12px;cursor:pointer;transition:all .15s}
.sess-chip:hover{background:var(--chip-hover-bg);border-color:var(--chip-hover-border);transform:translateY(-1px)}
.sc-date{font-size:10px;color:var(--chip-date);margin-bottom:3px;letter-spacing:.03em;transition:color .3s}
.sc-key{font-family:'Cormorant Garamond',serif;font-size:26px;font-weight:600;font-style:italic;color:var(--text-primary);line-height:1;transition:color .3s}
.sc-stems{font-size:10px;color:var(--chip-stems);margin-top:2px;transition:color .3s}
.card-btn{width:100%;background:var(--btn-bg);border:1px solid var(--btn-border);border-radius:12px;padding:10px;font-size:13px;font-weight:500;color:var(--btn-text);cursor:pointer;font-family:'Inter',sans-serif;transition:all .15s}
.card-btn:hover{background:var(--btn-hover-bg);color:var(--btn-hover-text);border-color:var(--btn-hover-border)}
.card.sunday .card-btn{background:var(--btn-sun-bg);border-color:var(--btn-sun-border);color:var(--btn-sun-text)}
.card.sunday .card-btn:hover{background:var(--btn-sun-hover)}
.empty-note{grid-column:1/-1;text-align:center;color:var(--text-dim);font-size:14px;padding:60px 20px}

/* admin: add-song card + per-card controls */
.add-card{border:1.5px dashed var(--accent-border);border-radius:20px;background:var(--accent-bg);display:flex;align-items:center;justify-content:center;min-height:210px;transition:border-color .2s,background .2s,transform .2s}
.add-card:hover{transform:translateY(-4px);border-color:var(--accent);background:var(--accent-badge-bg)}
.add-card:focus-visible{outline:2px solid var(--accent);outline-offset:3px}
.add-glass{display:flex;flex-direction:column;align-items:center;gap:8px;padding:24px;text-align:center}
.add-plus{width:46px;height:46px;border-radius:14px;background:var(--icon-bg);border:1px solid var(--icon-border);color:var(--accent);font-size:28px;line-height:1;display:flex;align-items:center;justify-content:center;font-weight:300}
.add-label{font-family:'Cormorant Garamond',serif;font-size:21px;font-weight:600;color:var(--accent-deep)}
.add-hint{font-size:12px;color:var(--text-dim);max-width:180px;line-height:1.4}
.no-sess{font-size:12.5px;color:var(--text-dim);background:var(--chip-bg);border:1px solid var(--chip-border);border-radius:12px;padding:14px;text-align:center;margin-bottom:14px}
.card-admin{display:flex;gap:6px;margin-top:12px;padding-top:12px;border-top:1px solid var(--divider)}
.ca-btn{flex:1;font-family:inherit;font-size:11.5px;font-weight:500;color:var(--text-secondary);background:var(--btn-bg);border:1px solid var(--btn-border);border-radius:9px;padding:7px 4px;cursor:pointer;transition:all .12s}
.ca-btn:hover{background:var(--btn-hover-bg);color:var(--text-primary);border-color:var(--btn-hover-border)}
.ca-btn.danger{color:var(--danger)}
.ca-btn.danger:hover{background:var(--danger-bg);border-color:var(--danger-border)}

/* modal form extras */
.check-row{display:flex;align-items:center;gap:10px;font-size:13.5px;color:var(--text-secondary);cursor:pointer}
.check-row input{width:16px;height:16px;accent-color:var(--accent);cursor:pointer}
.field-hint{font-size:11.5px;color:var(--text-faint);line-height:1.45;margin-top:6px}
.tok-grid{display:flex;flex-wrap:wrap;gap:7px}
.tok-chip{font-family:inherit;font-size:12px;font-weight:500;color:var(--pill-text);background:var(--pill-bg);border:1px solid var(--pill-border);border-radius:999px;padding:7px 13px;cursor:pointer;transition:all .12s}
.tok-chip:hover{border-color:var(--accent-border);color:var(--text-primary)}
.tok-chip.on{background:var(--accent-bg);border-color:var(--accent-border);color:var(--accent-deep);font-weight:600}
.dd-note{font-size:11.5px;color:var(--text-dim);line-height:1.45;padding:2px 10px 8px}
.act-tag{display:inline-block;font-size:11px;font-weight:600;color:var(--text-primary)}
.modal-sub{margin-top:4px;padding-top:16px;border-top:1px solid var(--divider)}
.acct-info{display:flex;flex-direction:column;gap:2px;margin-bottom:4px}
.ai-row{display:flex;align-items:center;justify-content:space-between;gap:16px;padding:11px 2px;border-bottom:1px solid var(--divider)}
.ai-row:last-child{border-bottom:none}
.ai-lab{font-size:11px;letter-spacing:.05em;text-transform:uppercase;color:var(--text-dim)}
.ai-val{font-size:14px;color:var(--text-primary);font-weight:500}
.ai-empty{color:var(--text-faint);font-weight:400;font-style:italic}
.acct-security{display:flex;gap:10px;margin-top:16px;padding-top:16px;border-top:1px solid var(--divider)}
.acct-security .btn{flex:1 1 0}
.acct-danger{margin-top:22px;padding:18px;border-radius:14px;border:1px solid var(--danger-border);background:var(--danger-bg)}
.ad-head{font-size:11px;letter-spacing:.07em;text-transform:uppercase;font-weight:600;color:var(--danger);margin-bottom:6px}
.ad-note{font-size:12.5px;color:var(--text-secondary);line-height:1.5;margin-bottom:14px}
.acct-danger .btn{width:100%}
.acct-danger .ad-note:last-child{margin-bottom:0}
.acct-note{margin-top:22px;padding:16px 18px;border-radius:14px;border:1px solid var(--card-border);background:var(--chip-bg)}
.an-head{font-size:11px;letter-spacing:.07em;text-transform:uppercase;font-weight:600;color:var(--text-secondary);margin-bottom:6px}
.an-note{font-size:12.5px;color:var(--text-dim);line-height:1.5}
.acct-note .an-note{margin-bottom:0}   /* it's the last thing in the box; kill the inherited .modal p bottom margin */

/* ── BUTTONS (shared) ─────────────────────────────────────── */
.btn{font-family:inherit;font-size:13px;font-weight:500;border-radius:11px;padding:10px 16px;cursor:pointer;transition:all .15s;border:1px solid var(--btn-border);background:var(--btn-bg);color:var(--btn-text)}
.btn:hover{background:var(--btn-hover-bg);color:var(--btn-hover-text);border-color:var(--btn-hover-border)}
.btn.primary{background:var(--primary-bg);border-color:transparent;color:var(--primary-text);font-weight:600}
.btn.primary:hover{background:var(--primary-hover);color:var(--primary-text)}
.btn.danger{background:var(--danger-bg);border-color:var(--danger-border);color:var(--danger)}
.btn.danger:hover{background:var(--danger-hover);color:var(--danger)}
.btn.ghost{background:none;border-color:transparent;color:var(--text-secondary)}
.btn.ghost:hover{background:var(--link-hover-bg);color:var(--text-primary)}
/* Solid fill, matching .btn.primary's own pattern — this is meant to look
   like an actual Planning Center green button, not a subtle tinted hint
   of one. */
.btn.pco{background:var(--pco-green);border-color:transparent;color:#fff;font-weight:600}
.btn.pco:hover{background:var(--pco-green-hover);color:#fff}
/* Small inline marker for a verified-via-Planning-Center request, used in
   list/modal contexts where a full card would be too heavy — the account
   requests list, the approve-request modal. */
.pco-badge{display:inline-flex;align-items:center;gap:4px;font-size:10.5px;font-weight:600;color:var(--pco-charcoal);background:var(--pco-green-bg);border:1px solid var(--pco-green-border);border-radius:999px;padding:3px 9px 3px 7px;white-space:nowrap}
.pco-badge svg{flex:0 0 auto;color:var(--pco-green)}
/* The full-size treatment for My Account's "connected" state — an actual
   card, not a small pill, using the real charcoal/grey PCO neutrals for
   its text so it visually reads as Planning Center's own palette rather
   than Sanctuary's purple-tinted one. */
/* The full-size treatment for a "connected" status — a soft gradient wash
   (fading into the surrounding card, not a flat color block) with a subtle
   shadow matching the quiet depth every other card in this app already
   has. Used in both My Account (no action) and Sanctuary Settings (with a
   Disconnect button via .pco-linked-actions, laid out inline in the same
   row rather than stacked below it). */
.pco-linked-card{display:flex;align-items:center;gap:14px;padding:18px 20px;border-radius:12px;background:linear-gradient(120deg, var(--pco-green-bg), transparent 75%);border:1px solid var(--pco-green-border);box-shadow:0 2px 10px var(--card-shadow)}
/* Matches .card-icon/.login-emblem exactly (soft tint + colored glyph on
   top) rather than a solid fill — this app never uses solid-color icon
   circles anywhere else, so a flat green disc read as visually foreign. */
.pco-linked-icon{flex:0 0 auto;width:32px;height:32px;border-radius:50%;background:var(--pco-green-bg);border:1px solid var(--pco-green-border);display:flex;align-items:center;justify-content:center;color:var(--pco-green)}
.pco-linked-title{font-size:13px;font-weight:700;color:var(--pco-charcoal)}
.pco-linked-sub{font-size:11.5px;color:var(--pco-grey);margin-top:2px;line-height:1.4}
.pco-linked-actions{margin-left:auto;flex:0 0 auto;padding-left:8px}
/* .needs-reconnect swaps the whole card from PCO green to the existing
   danger family — same structure, same soft-gradient background, same
   tint-circle icon (see .pco-linked-icon's own comment above for why it's a
   tint, not a solid fill), just a different color family entirely, so this
   state reads as "needs attention" at a glance rather than looking like a
   slightly different flavor of "all good". Title/sub text color stay on
   --pco-charcoal/--pco-grey like the healthy card — only the accent (icon,
   background wash, border) carries the warning, keeping the body text
   legible in both light and dark mode without a second pair of text tokens
   to maintain. */
.pco-linked-card.needs-reconnect{background:linear-gradient(120deg, var(--danger-bg), transparent 75%);border-color:var(--danger-border)}
.pco-linked-card.needs-reconnect .pco-linked-icon{background:var(--danger-bg);border-color:var(--danger-border);color:var(--danger)}
.btn.sm{padding:6px 12px;font-size:12px;border-radius:9px}
.btn:disabled{opacity:.4;cursor:not-allowed}

/* ── INPUTS (shared) ──────────────────────────────────────── */
.field{display:flex;flex-direction:column;gap:6px}
.field label{font-size:11px;letter-spacing:.04em;text-transform:uppercase;color:var(--text-dim);font-weight:500}
.input,select.input{font-family:inherit;font-size:14px;line-height:1.3;color:var(--input-text);background:var(--input-bg);border:1px solid var(--input-border);border-radius:11px;padding:11px 13px;width:100%;transition:border-color .15s,box-shadow .15s}
.input:focus,select.input:focus{outline:none;border-color:var(--input-focus);box-shadow:0 0 0 3px var(--accent-bg)}
.input::placeholder{color:var(--text-faint)}
/* Keep Brave/Chrome autofill from recoloring or resizing the field. (The
   browser's own "save password?" prompt is browser chrome and isn't stylable.) */
.input:-webkit-autofill,
.input:-webkit-autofill:hover,
.input:-webkit-autofill:focus{
  -webkit-text-fill-color:var(--input-text);
  -webkit-box-shadow:0 0 0 1000px var(--input-bg) inset;
  caret-color:var(--input-text);
}
select.input{cursor:pointer;appearance:none;background-image:linear-gradient(45deg,transparent 50%,var(--text-dim) 50%),linear-gradient(135deg,var(--text-dim) 50%,transparent 50%);background-position:calc(100% - 18px) center,calc(100% - 13px) center;background-size:5px 5px,5px 5px;background-repeat:no-repeat;padding-right:34px}

/* ── LOGIN ────────────────────────────────────────────────── */
.login-wrap{min-height:100vh;display:flex;align-items:center;justify-content:center;padding:24px}
.login-card{width:100%;max-width:430px;border-radius:28px;border:1px solid var(--card-border);background:var(--card-bg);backdrop-filter:blur(24px);-webkit-backdrop-filter:blur(24px);box-shadow:0 18px 60px var(--card-shadow);padding:52px 46px 30px}
.login-emblem{width:66px;height:66px;border-radius:20px;margin:0 auto 22px;background:var(--icon-bg);border:1px solid var(--icon-border);display:flex;align-items:center;justify-content:center;box-shadow:0 4px 18px var(--card-shadow)}
.login-emblem .letter{font-family:'Cormorant Garamond',serif;font-size:22px;font-weight:600;letter-spacing:.04em;color:var(--icon-text);line-height:1}
.login-mark{font-family:'Cormorant Garamond',serif;font-size:38px;font-weight:600;color:var(--text-primary);text-align:center;letter-spacing:.01em;line-height:1.05}
.login-mark em{font-style:italic;color:var(--accent)}
.login-sub{text-align:center;color:var(--text-dim);font-size:13.5px;margin-top:9px;margin-bottom:36px}
.login-form{display:flex;flex-direction:column;gap:18px}
.login-form .field label{margin-bottom:1px}
.login-form .input{padding:13px 15px;font-size:15px;border-radius:13px}
.login-err{font-size:12.5px;color:var(--danger);background:var(--danger-bg);border:1px solid var(--danger-border);border-radius:10px;padding:9px 12px;display:none}
.login-err.show{display:block}
.login-go{width:100%;padding:14px;font-size:14.5px;border-radius:13px;margin-top:6px}
.login-foot{text-align:center;font-size:11.5px;color:var(--text-faint);margin-top:30px;line-height:1.5}
.login-theme{display:flex;align-items:center;justify-content:center;margin-top:26px;padding-top:24px;border-top:1px solid var(--divider)}
.login-theme .toggle-wrap{margin:0;padding:0;border-left:0}

/* ── MIXER ────────────────────────────────────────────────── */
.mx{padding:28px 40px 56px;max-width:1500px;margin:0 auto}
.mx-top{display:flex;align-items:flex-start;justify-content:space-between;gap:20px;margin-bottom:8px;flex-wrap:wrap}
.mx-back{display:inline-flex;align-items:center;gap:6px;font-size:13px;color:var(--link-text);background:none;border:none;cursor:pointer;font-family:inherit;padding:6px 10px;border-radius:9px;margin-left:-10px;margin-bottom:10px;transition:background .15s,color .15s}
.mx-back:hover{background:var(--link-hover-bg);color:var(--link-hover-text)}
.mx-title{font-family:'Cormorant Garamond',serif;font-size:40px;font-weight:400;color:var(--text-primary);line-height:1.05}
.mx-title em{font-style:italic;color:var(--accent)}
.mx-artist{font-size:13px;color:var(--text-dim);margin-top:3px}
.mx-tag{display:inline-flex;align-items:center;gap:7px;font-size:11px;letter-spacing:.04em;color:var(--accent-badge-text);background:var(--accent-badge-bg);border:1px solid var(--accent-border);border-radius:999px;padding:5px 12px;margin-top:14px}
.mx-tag .dot{width:6px;height:6px;border-radius:50%;background:var(--accent)}

/* session switcher — also reused, unaltered, by the mix pill (#mix-wrap
   builds the exact same .sw-pill/.sw-block/.sw-actions markup), so this
   treatment applies to both and they keep reading as one matched set with
   the Mix/Studio pill next to them (§7.3). Vertical dividers replace the
   old flat gap:14px. Each block gets padding on BOTH sides now (not just
   before the divider) — the first pass only padded the side after a
   divider, so text still butted straight up against the rule with no
   breathing room on its own side; this is what actually read as cramped.
   align-items:stretch + justify-content:center per block is what makes the
   dividers run the pill's full height rather than stopping at the text. */
.sw-wrap{position:relative;display:flex;flex-direction:column;gap:9px;align-items:flex-end}
.sw-pill{display:flex;align-items:stretch;height:52px;box-sizing:border-box;background:var(--strip-bg);border:1px solid var(--strip-border);border-radius:16px;padding:0 22px;backdrop-filter:blur(12px)}
.sw-block{display:flex;flex-direction:column;justify-content:center;gap:2px;text-align:right;padding:0 20px}
.sw-block:first-child{padding-left:0}
.sw-block+.sw-block,.sw-actions+.sw-block{border-left:1px solid var(--divider)}
.sw-lab{font-size:10px;letter-spacing:.08em;text-transform:uppercase;color:var(--text-faint)}
.sw-date{font-size:13px;color:var(--text-secondary);font-weight:500;min-width:64px;display:inline-block}
.sw-key{font-family:'Cormorant Garamond',serif;font-style:italic;font-size:30px;font-weight:600;color:var(--accent);line-height:.9}
.sw-actions{display:flex;align-items:stretch;gap:8px}
.sw-actions .btn{height:100%;box-sizing:border-box;display:flex;align-items:center;justify-content:center;flex:1 1 auto;padding-left:18px;padding-right:18px}
/* :not(:empty) so a pill with no buttons (e.g. a volunteer on a song with
   only one session, so no Switch button, or without editKey, so no Correct
   key button) never shows a trailing divider with nothing after it — the
   div is still emitted by renderSwitcher()/renderMixPill() either way,
   this just collapses it to nothing when there's no button inside. There
   can be two of these now (Switch after Session, Correct key after Key),
   independently empty or not. No right-padding here deliberately — the
   pill's own outer edge padding (.sw-pill's 22px) already provides that
   spacing, so adding more here was doubling up on the same edge and is
   exactly what left the button visibly short of the space it was
   actually allotted. Only left-padding remains, for clearance from the
   divider line itself. */
.sw-actions:not(:empty){padding:0 0 0 16px;border-left:1px solid var(--divider)}
.sw-actions:empty{display:none}
/* Only the LAST slot's emptiness should pull in the trailing edge — if
   Switch is hidden but Correct key isn't, the divider before Key still
   needs to happen and Key still needs its own right-hand padding before
   Correct key, so this must check specifically for a trailing empty
   slot, not just "any empty slot anywhere in the pill". */
.sw-pill:has(.sw-actions:empty:last-child) .sw-block:last-of-type{padding-right:0}
/* Positioning (position/top/left) is entirely JS-driven now — see
   openAnchoredDD/positionDD in ui-mixer.js, which set position:fixed and
   compute left/top directly via inline style based on the trigger's real
   on-screen position. This rule is only ever responsible for appearance.
   Previously carried position:absolute/top/right left over from an older,
   below-the-trigger opening scheme — harmless for position/top since
   inline style always wins for whichever property it sets, but right:0
   was never overridden by anything, and having both left (inline) and
   right (here) set on a fixed element with no explicit width forces the
   browser to stretch it to fill the entire gap between them — exactly
   what caused dropdowns to render as a full-width bar pinned to the left
   edge instead of a compact panel next to their trigger icon. */
.dd{z-index:80;min-width:250px;background:var(--menu-bg);backdrop-filter:blur(20px);-webkit-backdrop-filter:blur(20px);border:1px solid var(--menu-border);border-radius:14px;box-shadow:0 16px 44px var(--menu-shadow);padding:6px;max-height:340px;overflow:auto}
.dd-title{font-size:10px;letter-spacing:.08em;text-transform:uppercase;color:var(--text-faint);padding:8px 10px 6px}
.dd.pco-dd .dd-title{color:var(--pco-green);font-size:14px;font-weight:700}
.dd-item{display:flex;align-items:center;justify-content:space-between;gap:12px;width:100%;background:none;border:none;font-family:inherit;text-align:left;padding:9px 11px;border-radius:10px;cursor:pointer;transition:background .12s;color:var(--text-secondary)}
.dd-item:hover{background:var(--link-hover-bg)}
.dd-item.cur{background:var(--accent-bg)}
.dd-item .d{font-size:12.5px;color:var(--text-secondary)}
.dd-item .k{font-family:'Cormorant Garamond',serif;font-style:italic;font-size:20px;font-weight:600;color:var(--accent)}
/* Fixed pixel columns, not 1fr — a 1fr grid's width depends on
   shrink-to-fit around whatever's in the dropdown at the time, which has
   no deterministic size. Fixed columns give the grid (and therefore the
   dropdown containing it) a predictable, consistent width regardless of
   content. */
.dd-grid{display:grid;grid-template-columns:repeat(4, 52px);gap:4px;padding:4px}
.kbtn{font-family:'Cormorant Garamond',serif;font-style:italic;font-size:14px;font-weight:600;color:var(--text-secondary);background:var(--ctrl-bg);border:1px solid var(--ctrl-border);border-radius:8px;padding:6px 0;cursor:pointer;transition:all .12s}
.kbtn:hover{background:var(--accent-bg);border-color:var(--accent-border);color:var(--accent)}
.kbtn.cur{background:var(--accent-bg);border-color:var(--accent-border);color:var(--accent)}

/* console */
.console{margin-top:22px;border-radius:22px;border:1px solid var(--strip-border);background:var(--strip-bg);backdrop-filter:blur(20px);-webkit-backdrop-filter:blur(20px);box-shadow:0 4px 30px var(--card-shadow);padding:22px;transition:background .3s,border-color .3s}

/* transport */
/* No border-bottom and no margin here on purpose — the horizontal rule under
   the transport IS .load-progress below, not a separate line. They used to be
   two stacked elements (a border on this row, then the loading bar beneath
   it), which spent ~20px of vertical space on what reads as one divider and
   left an obvious gap between the timeline and the console below. */
.transport{display:flex;align-items:center;gap:18px;padding:4px 4px 18px}
.play-btn{width:52px;height:52px;border-radius:50%;flex-shrink:0;background:var(--primary-bg);color:var(--primary-text);border:none;cursor:pointer;display:flex;align-items:center;justify-content:center;transition:background .15s,transform .1s}
.play-btn:hover{background:var(--primary-hover)}
.play-btn:active{transform:scale(.94)}
.play-btn svg{width:20px;height:20px;display:block}
.scrub{flex:1;display:flex;align-items:center;gap:14px}
.scrub-time{font-size:12px;color:var(--text-secondary);font-variant-numeric:tabular-nums;min-width:38px}
.scrub-time.end{text-align:right;color:var(--text-dim)}
.scrub-track{position:relative;flex:1;height:8px;border-radius:6px;background:var(--scrub-track);cursor:pointer}
.scrub-fill{position:absolute;left:0;top:0;bottom:0;border-radius:6px;background:var(--scrub-fill);width:0%}
.scrub-handle{position:absolute;top:50%;width:16px;height:16px;border-radius:50%;background:var(--scrub-handle);border:2px solid var(--bg);transform:translate(-50%,-50%);left:0%;box-shadow:0 1px 4px rgba(0,0,0,.2)}

/* stem-loading progress bar — sits between the transport and the console.
   Collapses to zero height/margin when done, so once loading finishes the
   layout is pixel-identical to how it looked before this bar existed. */
/* One element doing two jobs: a 3px progress bar while stems stream and
   decode, then — once they're all in — it settles into the 1px rule that
   separates the transport from everything below it. It never disappears and
   the margin never changes, so nothing under it shifts when loading finishes;
   only the bar's own height and colour animate. This is why .transport above
   carries no border of its own. */
.load-progress{height:3px;border-radius:3px;background:var(--scrub-track);overflow:hidden;margin:0 0 22px;transition:height .35s ease,background-color .35s ease,border-radius .35s ease}
.load-progress-fill{height:100%;border-radius:3px;background:var(--scrub-fill);width:0%;transition:width .15s linear,opacity .3s ease}
.load-progress.done{height:1px;border-radius:0;background:var(--divider)}
/* The fill sits at 100% when loading completes; fading it out is what lets
   the track underneath read as a plain divider rather than a full bar. */
.load-progress.done .load-progress-fill{opacity:0}

.transport-hint{font-size:11px;color:var(--text-faint);white-space:nowrap}

/* strips */
.strips{display:flex;gap:14px;align-items:stretch;overflow-x:auto;padding-bottom:6px}
.strip{position:relative;flex:0 0 auto;width:118px;display:flex;flex-direction:column;align-items:center;gap:12px;background:var(--strip-inner);border:1px solid var(--strip-border);border-radius:16px;padding:16px 12px}
.strip.master{width:128px;background:var(--accent-bg);border-color:var(--accent-border)}
/* ---- admin manage mode (stem delete / session delete) ---- */
/* Manage-stems toggle + its fold-out panel. The toggle is a fixed 128px —
   exactly the master strip's width (.strip.master below) — so it visually
   anchors directly above it rather than just being as-wide-as-its-text. The
   panel (Add stem / Delete session / hint) unfolds open to its right on
   click rather than snapping in instantly.
   No horizontal padding here (unlike before) — .strips below has none
   either, so the toggle's left edge lines up exactly with where the master
   strip starts, rather than sitting a few px to the right of it. */
.mx-admin{display:flex;align-items:center;gap:20px;padding:0 0 10px;margin-top:-6px}
.mng-group{display:flex;align-items:center}
.master-dl-btn{width:100%;margin-top:10px;font-size:11px;padding:7px 6px;background:var(--card-bg);border:1px solid var(--strip-border);color:var(--text-dim)}
.master-dl-btn:hover{background:var(--pill-on-bg);color:var(--text-primary)}

.mng-toggle{
  width:128px;height:32px;flex:0 0 auto;
  display:flex;align-items:center;justify-content:center;gap:5px;
  background:var(--accent-bg);border:1px solid var(--accent-border);
  border-radius:11px;padding:0 10px;
  font-family:inherit;font-size:11px;font-weight:600;color:var(--accent);
  white-space:nowrap;box-shadow:0 2px 8px var(--card-shadow);
  cursor:pointer;transition:background .15s ease,border-color .15s ease,transform .1s ease,box-shadow .15s ease;
}
.mng-toggle:hover{background:var(--pill-on-bg);border-color:var(--pill-on-border);box-shadow:0 3px 10px var(--card-shadow)}
.mng-toggle:active{transform:scale(.97)}
.mng-toggle-chevron{flex:0 0 auto;transition:transform .28s ease-out}
.console.managing .mng-toggle-chevron{transform:rotate(180deg)}

/* max-width caps close to the panel's actual natural content width (two
   buttons + the hint, see .mng-hint below) rather than an oversized ceiling
   — with a big ceiling, the box visually finishes opening well before the
   animated value itself reaches its target, so the transition keeps
   "running" invisibly after the motion has already stopped, which is part
   of what made this feel clunky. Timing/easing now matches the strip
   transitions below (.28s ease-out) so everything settles on the same beat
   instead of the panel finishing noticeably later than the strips around it. */
.mng-panel{
  display:flex;align-items:center;gap:14px;flex-wrap:nowrap;white-space:nowrap;
  max-width:0;max-height:40px;opacity:0;overflow:hidden;margin-left:0;
  transition:max-width .28s ease-out,opacity .18s ease-out .06s,margin-left .28s ease-out;
}
.console.managing .mng-panel{max-width:620px;opacity:1;margin-left:14px}
/* Every control in this row shares one explicit height. .mng-toggle and
   .btn.sm were previously sized by their own padding and font-size, which
   differ, so they landed ~3px apart and the row read as slightly ragged.
   A fixed height with flex centring makes that impossible regardless of
   label length or font. */
.mng-panel .btn.sm{height:32px;display:inline-flex;align-items:center;padding:0 12px}
/* Fixed width, NOT max-width — this is the actual fix for the hint text
   wrapping into 3 lines and then re-wrapping into 2 as the animation plays.
   A max-width lets the browser recompute the wrap point continuously as the
   ANIMATING PARENT's available space changes; a fixed width (plus
   flex-shrink:0 so the flex layout can't compress it either) means the line
   breaks are decided ONCE, immediately, and never move — the open animation
   just reveals progressively more of an already-stable two-line block
   instead of text visibly re-flowing while it plays. */
.mng-hint{font-size:11.5px;color:var(--text-dim);line-height:1.3;white-space:normal;width:190px;flex-shrink:0}
.strip-del{display:none;position:absolute;top:7px;right:7px;width:21px;height:21px;border-radius:50%;border:1px solid var(--danger-border);background:var(--danger-bg);color:var(--danger);font-size:11px;line-height:1;cursor:pointer;align-items:center;justify-content:center;padding:0;z-index:4;transition:background .15s,color .15s,transform .12s}
.strip-del:hover{background:var(--danger);color:#fff;transform:scale(1.08)}
.console.managing .strip-del{display:flex}
.strip-grip{display:none;position:absolute;top:8px;left:8px;width:24px;height:20px;align-items:center;justify-content:center;cursor:grab;color:var(--text-faint);font-size:14px;line-height:1;letter-spacing:-1px;user-select:none;touch-action:none;z-index:5}
.strip-grip:hover{color:var(--text-dim)}
.strip-grip:active{cursor:grabbing}
.console.managing .strip-grip{display:flex}
/* manage mode: reserve a top band so the grip + ✕ never overlap the name,
   and give the strips a little more room so they don't feel cramped */
.console.managing .strips{gap:18px}
.console.managing .strip:not(.master){padding-top:36px}
.console.managing .strip-del{top:8px;right:8px}
.console.managing .strip{border-color:var(--danger-border)}
.console.managing .strip.master{border-color:var(--accent-border)}
/* the grabbed strip lifts off the page and follows the pointer */
.strip.lifting{z-index:9999;box-shadow:0 20px 44px rgba(15,14,25,.34);border-color:var(--accent-border)!important;background:var(--strip-bg);opacity:.97;transform:rotate(1.4deg);transform-origin:top left}
.strip.lifting .strip-grip{display:flex}
.strip.lifting .strip-del{display:flex}
[data-mode="dark"] .strip.lifting{box-shadow:0 20px 50px rgba(0,0,0,.6)}
/* placeholder that holds the grabbed strip's slot */
.strip-placeholder{flex:0 0 auto;align-self:stretch;border-radius:16px;border:2px dashed var(--accent-border);background:var(--accent-bg);opacity:.6}
.strip-name{display:flex;flex-direction:column;align-items:center;gap:3px;text-align:center;min-height:42px;justify-content:flex-start}
.strip-name .sn-main{font-size:12.5px;font-weight:600;color:var(--text-primary);line-height:1.15}
.strip-tok{font-size:9px;letter-spacing:.1em;text-transform:uppercase;color:var(--text-faint);background:var(--chip-bg);border-radius:5px;padding:1px 6px}
/* pre-amp knob */
.knob-wrap{display:flex;justify-content:center}
.knob-wrap-inner{display:flex;flex-direction:column;align-items:center;gap:4px}
.knob-lab{font-size:8.5px;letter-spacing:.12em;text-transform:uppercase;color:var(--text-faint)}
.knob{width:46px;height:46px;border-radius:50%;background:radial-gradient(circle at 50% 38%, var(--knob-hi,rgba(255,255,255,.18)), transparent 60%),var(--ctrl-bg);border:1px solid var(--ctrl-border);cursor:ns-resize;touch-action:none;position:relative;display:flex;align-items:center;justify-content:center;box-shadow:0 2px 6px var(--card-shadow),inset 0 1px 2px rgba(255,255,255,.15)}
.knob:focus-visible{outline:2px solid var(--accent);outline-offset:2px}
.knob-dial{width:100%;height:100%;position:absolute;inset:0;transform:rotate(0deg)}
.knob-ind{position:absolute;left:50%;top:5px;width:2.5px;height:14px;border-radius:2px;background:var(--accent);transform:translateX(-50%);box-shadow:0 0 4px var(--accent)}
.knob-val{font-size:10px;font-variant-numeric:tabular-nums;color:var(--text-secondary);height:13px}
.knob-val.boost{color:var(--accent);font-weight:600}
.fader-area{display:flex;gap:10px;align-items:stretch;height:200px}
.vu{width:15px;height:100%;border-radius:6px;background:var(--strip-inner);border:1px solid var(--strip-border);display:flex;flex-direction:column-reverse;gap:2px;padding:3px;overflow:hidden}
.vu .led{flex:1;border-radius:2px;background:var(--vu-off);transition:background .05s linear}
.fader{position:relative;width:46px;height:100%;cursor:pointer;touch-action:none}
.fader-track{position:absolute;left:50%;top:0;bottom:0;width:7px;transform:translateX(-50%);border-radius:6px;background:var(--fader-track)}
.fader-fill{position:absolute;left:50%;width:7px;transform:translateX(-50%);bottom:0;border-radius:6px;background:var(--fader-fill)}
.fader-unity{position:absolute;left:50%;width:24px;height:1px;transform:translateX(-50%);background:var(--text-faint);opacity:.7}
.fader-cap{position:absolute;left:50%;width:42px;height:22px;transform:translate(-50%,50%);border-radius:7px;background:var(--fader-thumb);border:1.5px solid var(--fader-thumb-br);box-shadow:0 3px 8px rgba(0,0,0,.22),inset 0 1px 2px rgba(255,255,255,.3),inset 0 -2px 3px rgba(0,0,0,.18);cursor:grab}
.fader-cap:active{cursor:grabbing}
.fader-cap::after{content:'';position:absolute;left:8px;right:8px;top:50%;height:2px;border-radius:2px;background:var(--fader-thumb-br);opacity:.65;box-shadow:0 3px 0 rgba(0,0,0,.12),0 -3px 0 rgba(0,0,0,.12)}
.db-read{font-size:11px;font-variant-numeric:tabular-nums;color:var(--text-secondary);height:14px}
.ms-row{display:flex;gap:6px}
.ms{width:36px;height:28px;border-radius:8px;font-size:11px;font-weight:600;border:1px solid var(--ctrl-border);background:var(--ctrl-bg);color:var(--ctrl-text);cursor:pointer;transition:all .12s;font-family:inherit}
.ms:hover{border-color:var(--text-dim)}
.ms.mute.on{background:var(--mute-on-bg);border-color:var(--mute-on-br);color:var(--mute-on-text)}
.ms.solo.on{background:var(--solo-on-bg);border-color:var(--solo-on-br);color:var(--solo-on-text)}
.lim{display:flex;flex-direction:column;align-items:center;gap:3px;margin-top:2px}
.lim-led{width:9px;height:9px;border-radius:50%;background:var(--vu-off);transition:background .08s}
.lim-led.on{background:var(--lim-on);box-shadow:0 0 8px var(--lim-on)}
.lim-lab{font-size:8.5px;letter-spacing:.1em;color:var(--text-faint)}

/* ── CHANGE LOG ───────────────────────────────────────────── */
.panel{max-width:1100px;margin:0 auto;padding:0 40px 60px}
.log-table{width:100%;border-collapse:collapse}
.log-card{border-radius:18px;border:1px solid var(--card-border);background:var(--card-bg);backdrop-filter:blur(20px);box-shadow:0 2px 24px var(--card-shadow);overflow:hidden}
.tbl{width:100%;border-collapse:collapse;font-size:13px}
.tbl th{text-align:left;font-size:10px;letter-spacing:.07em;text-transform:uppercase;color:var(--table-head);font-weight:600;padding:14px 18px;border-bottom:1px solid var(--table-border)}
.tbl td{padding:14px 18px;border-bottom:1px solid var(--table-border);color:var(--text-secondary);vertical-align:middle}
.tbl tr:last-child td{border-bottom:none}
.tbl tr:hover td{background:var(--row-hover)}
.who{display:flex;flex-direction:column;gap:2px}
.who .nm{color:var(--text-primary);font-weight:500}
.chg{display:inline-flex;align-items:center;gap:8px;font-family:'Cormorant Garamond',serif;font-style:italic;font-size:18px}
.chg .from{color:var(--text-dim)}
.chg .arr{font-size:12px;color:var(--text-faint);font-family:'Inter'}
.chg .to{color:var(--accent)}
.role-tag{display:inline-block;font-size:10px;letter-spacing:.05em;text-transform:uppercase;font-weight:600;padding:2px 8px;border-radius:999px;background:var(--role-bg);color:var(--role-text)}
.ts-cell{font-variant-numeric:tabular-nums;color:var(--text-dim);font-size:12px;white-space:nowrap}
.panel-empty{padding:56px 24px;text-align:center;color:var(--text-dim)}
.panel-empty .big{font-family:'Cormorant Garamond',serif;font-size:24px;font-style:italic;color:var(--text-secondary);margin-bottom:8px}

/* ── ACCOUNTS ─────────────────────────────────────────────── */
.acct-add{border-radius:18px;border:1px solid var(--card-border);background:var(--card-bg);backdrop-filter:blur(20px);box-shadow:0 2px 24px var(--card-shadow);padding:22px;margin-bottom:18px}
.acct-add h3{font-family:'Cormorant Garamond',serif;font-size:22px;font-weight:600;margin-bottom:16px;color:var(--text-primary)}
.add-grid{display:grid;grid-template-columns:1fr 1fr 1.2fr 1fr auto auto;gap:12px;align-items:end}
/* .btn's own padding/line-height are a couple px shorter than .input's — match
   .input's vertical metrics exactly here so the Add button is precisely the
   same height as the fields beside it, not just visually close. */
.add-grid #na-go{padding:11px 18px;line-height:1.3}
.owner-tag{display:inline-block;font-size:10px;letter-spacing:.05em;text-transform:uppercase;font-weight:600;padding:2px 8px;border-radius:999px;background:var(--owner-bg);color:var(--owner-text);margin-left:8px}
.row-actions{display:flex;gap:7px;justify-content:flex-end;flex-wrap:wrap}
.inline-select{font-family:inherit;font-size:12px;color:var(--input-text);background:var(--input-bg);border:1px solid var(--input-border);border-radius:9px;padding:6px 26px 6px 10px;cursor:pointer;appearance:none;background-image:linear-gradient(45deg,transparent 50%,var(--text-dim) 50%),linear-gradient(135deg,var(--text-dim) 50%,transparent 50%);background-position:calc(100% - 13px) center,calc(100% - 9px) center;background-size:4px 4px,4px 4px;background-repeat:no-repeat}
.inline-select:disabled{opacity:.5;cursor:not-allowed}
.acct-foot{margin-top:22px;display:flex;justify-content:space-between;align-items:center;gap:16px;flex-wrap:wrap}
.acct-foot .note{font-size:11.5px;color:var(--text-faint);line-height:1.5;max-width:560px}

/* ── MODAL ────────────────────────────────────────────────── */
.overlay{position:fixed;inset:0;background:var(--overlay);backdrop-filter:blur(4px);z-index:200;display:flex;align-items:center;justify-content:center;padding:24px;animation:fade .15s ease}
/* Background scroll lock, applied to <html> whenever anything is mounted in
   #overlay-root — driven by UI._syncScrollLock in ui-core.js, which is where
   the reasoning lives. Deliberately overflow:hidden rather than the more
   common position:fixed-on-body recipe: nav is position:sticky, and taking
   the body out of flow leaves sticky with no scrolling ancestor, which would
   drop the nav out of view the instant any popup opened.
   overscroll-behavior stops the scroll chaining / rubber-band that would
   otherwise still move the page on touch devices. Descendants with their own
   overflow (.pco-pick-list, .pco-song-list, .dd panels) are unaffected and
   keep scrolling normally — this only pins the page behind them. */
html.scroll-locked{overflow:hidden;overscroll-behavior:none}
html.scroll-locked body{overflow:hidden}
@keyframes fade{from{opacity:0}to{opacity:1}}
/* display:flex;flex-direction:column + max-height + overflow:hidden here,
   paired with .modal-body's own flex:1 1 auto;overflow-y:auto below, is
   the actual fix: NOTHING previously capped a modal's height at all, so a
   body long enough (a MultiTracks song's 25+ file list, a large team's
   roster) simply grew the whole card past the viewport with no way to
   reach the rest of it — not a missing scrollbar so much as nothing ever
   telling the browser there was a boundary to scroll AGAINST. The h3 title
   and .modal-actions buttons are flex:0 0 auto (see their own rules) so
   they never shrink or scroll away — only the body between them does.
   max-height mirrors .overlay's own 24px top+bottom padding exactly, so
   the card never sits closer than that to the viewport edge either way.
   overflow:hidden here (not overflow-y:auto) is deliberate: THIS element
   never scrolls itself, it only needs to clip its rounded corners so a
   scrolling .modal-body's square edges can never visually poke past them
   mid-scroll. A short modal is completely unaffected — overflow-y:auto on
   .modal-body only engages once content actually exceeds the space, same
   as it always would for a properly working scroll container. */
.modal{display:flex;flex-direction:column;max-height:calc(100vh - 48px);overflow:hidden;width:100%;max-width:440px;background:var(--modal-bg);border:1px solid var(--modal-border);border-radius:22px;box-shadow:0 24px 70px rgba(0,0,0,.4);padding:28px;animation:pop .18s cubic-bezier(.34,1.3,.64,1)}
@keyframes pop{from{opacity:0;transform:scale(.96) translateY(6px)}to{opacity:1;transform:none}}
.modal h3{flex:0 0 auto;font-family:'Cormorant Garamond',serif;font-size:26px;font-weight:600;color:var(--text-primary);line-height:1.1;margin-bottom:10px}
.modal p{font-size:13.5px;color:var(--text-secondary);line-height:1.55;margin-bottom:20px}
.modal p strong{color:var(--text-primary)}
/* This selector never existed before — every .modal-body div in the app
   (UI.modal wraps any opts.body in one automatically) was rendering with
   zero rules attached to that class. min-height:0 is not optional: a flex
   child's default min-height is auto, which resolves to its own content's
   height and silently defeats flex-shrink — without it this would still
   try to grow to fit everything instead of stopping at the space actually
   available and scrolling the rest, the same flex trap documented on
   .pco-step's min-width earlier in this file. */
.modal-body{flex:1 1 auto;min-height:0;overflow-y:auto}
.modal-fields{display:flex;flex-direction:column;gap:14px;margin-bottom:22px}
.modal-actions{flex:0 0 auto;display:flex;gap:10px;justify-content:flex-end;margin-top:20px}

/* ── TOAST ────────────────────────────────────────────────── */
.toast-wrap{position:fixed;bottom:26px;left:50%;transform:translateX(-50%);z-index:300;display:flex;flex-direction:column;gap:8px;align-items:center}
.toast{background:var(--toast-bg);color:var(--toast-text);font-size:13px;font-weight:500;padding:12px 20px;border-radius:12px;box-shadow:0 10px 34px rgba(0,0,0,.3);animation:toastin .25s cubic-bezier(.34,1.3,.64,1);max-width:90vw}

@keyframes toastin{from{opacity:0;transform:translateY(10px)}to{opacity:1;transform:none}}

/* ── RESPONSIVE ───────────────────────────────────────────── */
@media (max-width:900px){
  nav{padding:16px 20px;flex-wrap:wrap;gap:10px}
  .pg-head{padding:36px 20px 22px;flex-direction:column;align-items:flex-start;gap:8px}
  .pg-head h1{font-size:40px}
  .pills{margin-left:20px;margin-right:20px;padding-left:0;padding-right:0}
  .pills{padding-left:20px;padding-right:20px}
  .grid{grid-template-columns:1fr;padding:0 20px 48px}
  .mx{padding:20px 16px 48px}
  .mx-title{font-size:30px}
  .sw-wrap{align-items:flex-start;width:100%}
  .mx-top{flex-direction:column}
  .panel{padding:0 16px 48px}
  .add-grid{grid-template-columns:1fr 1fr}
  .tbl th:nth-child(2),.tbl td:nth-child(2){display:none}
  .transport{flex-wrap:wrap}
  .transport-hint{display:none}
}
@media (prefers-reduced-motion:reduce){
  *{animation-duration:.001ms !important;transition-duration:.001ms !important}
}

/* ---- stem upload dropzone (Phase 2) ---- */
.dropzone{border:1.5px dashed var(--input-border);border-radius:12px;background:var(--input-bg);padding:18px 14px;text-align:center;transition:border-color .15s,background .15s}
.dropzone.drag{border-color:var(--accent);background:var(--accent-bg)}
.dz-prompt{font-size:13px;color:var(--text-dim)}
.dz-browse{font-family:inherit;font-size:13px;color:var(--accent);background:none;border:none;padding:0;cursor:pointer;text-decoration:underline}
.dz-list{display:flex;flex-direction:column;gap:6px;margin-top:12px}
.dz-list:empty{margin-top:0}
/* A dropzone row is now a two-line block (name/size/control on top, a
   progress rail underneath) rather than a single flex line, because a
   50-120 MB stem takes long enough that a static filename with no feedback
   reads as a frozen app. The rail is always in the DOM at 0% width and
   simply never moves for an upload that hasn't started — nothing appears or
   disappears mid-upload, so the row never changes height and the list never
   reflows under the pointer. */
.dz-item{display:flex;flex-direction:column;gap:6px;background:var(--card-bg);border:1px solid var(--card-border);border-radius:9px;padding:8px 11px;text-align:left}
.dz-item-top{display:flex;align-items:center;justify-content:space-between;gap:10px}
.dz-name{flex:1 1 auto;min-width:0;font-size:12.5px;color:var(--text);word-break:break-all}
.dz-size{flex:0 0 auto;font-size:11px;color:var(--text-faint);font-variant-numeric:tabular-nums}
/* tabular-nums so the percentage doesn't jitter the row's width as it counts
   up through 9% -> 10% -> 100%. */
.dz-pct{flex:0 0 auto;font-size:11px;font-weight:700;color:var(--accent);font-variant-numeric:tabular-nums;min-width:34px;text-align:right}
.dz-x{font-family:inherit;font-size:16px;line-height:1;color:var(--text-dim);background:none;border:none;cursor:pointer;padding:0 2px;flex:none}
.dz-x:hover{color:var(--danger,#c0392b)}
.dz-bar{height:3px;border-radius:3px;background:var(--ctrl-bg);overflow:hidden}
.dz-bar-fill{height:100%;width:0;border-radius:3px;background:var(--accent);transition:width .18s ease}
.dz-item-done .dz-bar-fill{background:var(--pco-green)}
.dz-item-done .dz-pct{color:var(--pco-green)}

/* ---- stem edit affordance + hidden stems (Manage stems) ---- */
/* Sits beside .strip-del and follows the same reveal rule — both only exist
   visually while the console is in manage mode, so an ordinary user mixing a
   session never sees either. */
/* Centred in the 36px manage-mode control band rather than tucked into a
   corner: .strip-grip already owns top-left (8/8) and .strip-del owns
   top-right (7/7), so both corners are taken. left:50% + translateX puts this
   in the gap between them at any strip width, and it shares .strip-del's
   z-index so it sits above the strip's own content the same way. */
.strip-edit{
  position:absolute;top:7px;left:50%;transform:translateX(-50%);
  width:21px;height:21px;border-radius:50%;
  display:none;align-items:center;justify-content:center;
  font-family:inherit;font-size:11px;line-height:1;padding:0;z-index:4;
  background:var(--ctrl-bg);border:1px solid var(--ctrl-border);color:var(--text-dim);
  cursor:pointer;transition:background .15s,color .15s,border-color .15s;
}
.managing .strip-edit{display:flex}
.strip-edit:hover{background:var(--accent-bg);border-color:var(--accent-border);color:var(--accent)}

.hs-list{display:flex;flex-direction:column;gap:8px}
.hs-row{
  display:flex;align-items:center;gap:12px;
  background:var(--card-bg);border:1px solid var(--card-border);
  border-radius:10px;padding:10px 12px;
}
.hs-main{flex:1 1 auto;min-width:0;display:flex;flex-direction:column;gap:2px}
.hs-name{font-size:13px;font-weight:700;color:var(--text-primary);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.hs-tok{font-size:10.5px;letter-spacing:.05em;text-transform:uppercase;color:var(--text-faint)}
.hs-actions{flex:0 0 auto;display:flex;gap:8px}

/* ---- credits panel (rail) ---- */
/* .dd carries a min-width but no max, so a long copyright line (they run to
   several publishers) stretched this panel most of the way across the page.
   Capped like .mix-dd is, and the value column wraps instead of pushing. */
.dd.credits-dd{max-height:min(420px, calc(100vh - 120px));overflow-y:auto;min-width:280px;max-width:min(380px, calc(100vw - 24px))}
.cr-block{display:flex;flex-direction:column;gap:2px;padding:2px 4px 6px}
.cr-row{display:flex;align-items:baseline;gap:10px;padding:5px 8px;border-radius:7px}
.cr-row:nth-child(odd){background:var(--strip-inner)}
/* The label column is fixed-width and the value takes the slack, so a long
   copyright line wraps under itself instead of pushing the label out of
   alignment with the rows above it. */
.cr-k{flex:0 0 72px;font-size:10.5px;letter-spacing:.04em;text-transform:uppercase;color:var(--text-faint)}
.cr-v{flex:1 1 auto;min-width:0;font-size:12.5px;color:var(--text-primary);word-break:break-word}
.cr-sep{padding:10px 12px 4px;font-size:10px;letter-spacing:.08em;text-transform:uppercase;color:var(--pco-green);font-weight:700}

/* ---- Planning Center maintenance actions (Sanctuary Settings) ---- */
.pco-tools{margin-top:16px;border:1px solid var(--card-border);border-radius:12px;padding:14px 16px;background:var(--card-bg)}
.pco-tools-label{font-size:10px;font-weight:700;letter-spacing:.06em;text-transform:uppercase;color:var(--text-faint);margin-bottom:10px}
.pco-tool{display:flex;align-items:center;gap:14px;padding:9px 0;border-top:1px solid var(--divider)}
.pco-tool:first-of-type{border-top:none;padding-top:0}
/* min-width:0 so a long description wraps inside its column instead of
   pushing the button off the row — the same flex trap as elsewhere in here. */
.pco-tool-text{flex:1 1 auto;min-width:0;display:flex;flex-direction:column;gap:2px}
.pco-tool-text strong{font-size:12.5px;font-weight:700;color:var(--text-primary)}
.pco-tool-text span{font-size:11.5px;color:var(--text-dim);line-height:1.4}
.pco-tool .btn.sm{flex:0 0 auto;height:30px;display:inline-flex;align-items:center;padding:0 14px}
.pco-tools-note{margin-top:12px;padding-top:10px;border-top:1px solid var(--divider);font-size:11px;color:var(--text-faint);line-height:1.45}

/* ---- quick-pick performer chips ---- */
.perf-chips{display:flex;flex-wrap:wrap;gap:6px;margin-top:8px}
.perf-chip{
  font-family:inherit;font-size:11.5px;line-height:1;
  padding:6px 11px;border-radius:999px;cursor:pointer;
  background:var(--pill-bg);border:1px solid var(--pill-border);color:var(--text-dim);
  transition:background .12s,border-color .12s,color .12s;
}
.perf-chip:hover{background:var(--pill-on-bg);border-color:var(--pill-on-border);color:var(--text-primary)}
/* Selected chips use the same green as the credits panel they feed, so the
   link between "tapped here" and "appears there" is visible without a label. */
.perf-chip.on{background:var(--pco-green);border-color:var(--pco-green);color:#fff;font-weight:600}

/* ---- instrument list editor (Sanctuary Settings) ---- */
.inst-list{display:flex;flex-direction:column;gap:8px;max-height:340px;overflow-y:auto}
/* Fixed columns rather than flex-grow on each: the code field only ever holds
   a short identifier, so letting it share space equally with the display name
   would waste the width where it's actually needed. */
.inst-row{display:grid;grid-template-columns:auto 108px 1fr auto;align-items:center;gap:8px}
.inst-ord{display:flex;flex-direction:column;gap:2px}
.inst-move{
  font-family:inherit;font-size:9px;line-height:1;padding:2px 5px;border-radius:5px;cursor:pointer;
  background:var(--pill-bg);border:1px solid var(--pill-border);color:var(--text-dim);
}
.inst-move:hover:not(:disabled){background:var(--pill-on-bg);color:var(--text-primary)}
.inst-move:disabled{opacity:.3;cursor:default}
.inst-row .input{padding:7px 10px;font-size:12.5px}
.inst-token{font-family:var(--mono, ui-monospace, monospace)}
.inst-del{
  font-family:inherit;font-size:13px;line-height:1;padding:0 6px;cursor:pointer;
  background:none;border:none;color:var(--text-dim);
}
.inst-del:hover{color:var(--danger)}
.play-btn.loading{opacity:.5;cursor:progress}

/* ── VIEW TRANSITIONS ─────────────────────────────────────── */
/* Each view (library, mixer, log, accounts, login) fades + rises in
   as it mounts. The sticky nav is intentionally NOT animated, so it
   stays anchored while the content below it changes.
   Tune the feel with the three values on the .view-anim line:
     translateY distance (8px) · duration (.34s) · easing curve.
   The prefers-reduced-motion block above already collapses this to
   an instant change for users who ask for less motion. */
.view-anim{animation:viewIn .34s cubic-bezier(.22,.61,.36,1) both}
@keyframes viewIn{
  from{opacity:0;transform:translateY(8px)}
  to{opacity:1;transform:translateY(0)}
}

/* ── OVERLAY / MODAL / MENU MOTION ────────────────────────── */
/* The user menu previously had no entrance — give it one that grows from
   the top-right, under the name button. Exit (.is-closing) animations are
   added by UI.dismissOverlay() and kept short so dismissing feels instant.
   The prefers-reduced-motion block above collapses all of these. */
.menu{transform-origin:top right;animation:menuIn .16s cubic-bezier(.34,1.3,.64,1)}
@keyframes menuIn{
  from{opacity:0;transform:scale(.96) translateY(-6px)}
  to{opacity:1;transform:none}
}
.overlay.is-closing{animation:fadeOut .16s ease forwards}
.modal.is-closing{animation:popOut .16s cubic-bezier(.4,0,.7,.4) forwards}
.menu.is-closing{animation:menuOut .14s ease forwards}
@keyframes fadeOut{from{opacity:1}to{opacity:0}}
@keyframes popOut{from{opacity:1;transform:none}to{opacity:0;transform:scale(.97) translateY(4px)}}
@keyframes menuOut{from{opacity:1;transform:none}to{opacity:0;transform:scale(.97) translateY(-6px)}}

/* ── LIBRARY CARD CASCADE ─────────────────────────────────── */
/* Cards fade + rise in sequence on load and filter changes (the per-card
   delay is set inline by renderGrid). `backwards` fill holds the hidden
   start state during the delay, then reverts cleanly to the base style so
   the hover-lift transform is never overridden. The reduced-motion rule
   below also zeroes the stagger delay (the global rule only kills duration). */
.card-in{animation:cardIn .4s cubic-bezier(.22,.61,.36,1) backwards}
@keyframes cardIn{
  from{opacity:0;transform:translateY(10px)}
  to{opacity:1;transform:translateY(0)}
}
@media (prefers-reduced-motion:reduce){
  .card-in{animation-delay:0ms !important}
}

/* ── HEADER SEARCH TWIN ───────────────────────────────────── */
/* The library search mirrored into the nav, on the LEFT next to the logo to
   match the inline bar's position. Collapsed to zero width when hidden so it
   takes no space; .show (toggled by the observer in UI.library) expands +
   fades it in. Disabled on narrow screens, where the nav wraps. */
.nav-left{display:flex;align-items:center;min-width:0}
.nav-search{
  max-width:0;opacity:0;margin-left:0;padding-left:0;padding-right:0;
  overflow:hidden;pointer-events:none;flex:0 0 auto;
  transition:max-width .3s cubic-bezier(.22,.61,.36,1),
             opacity .25s ease,
             margin-left .3s cubic-bezier(.22,.61,.36,1),
             padding .3s cubic-bezier(.22,.61,.36,1);
}
.nav-search.show{max-width:260px;opacity:1;margin-left:18px;padding-left:15px;padding-right:15px;pointer-events:auto}
.nav-search.nav-search-ghost{visibility:hidden;pointer-events:none}
.nav-search input{width:150px}
@media (max-width:900px){ .nav-search{display:none} }

/* ── SEARCH CLEAR BUTTON ──────────────────────────────────── */
/* SVG cross shown only when a search bar has text (.has-text).
   display:none -> display:flex so flexbox centres the SVG automatically. */
.search-clear{
  display:none;flex:0 0 auto;align-items:center;justify-content:center;
  width:18px;height:18px;border-radius:50%;border:none;cursor:pointer;
  padding:0;font-family:inherit;box-sizing:border-box;
  color:var(--text-dim);background:var(--pill-on-bg);transition:color .12s,background .12s;
}
/* Inline SVGs default to vertical-align:baseline, which reserves a few
   pixels of space below the glyph the way text reserves room for a
   descender (g, y, p...) — even though flexbox correctly centers the
   SVG's own BOX, that invisible reserved space makes the visible ink
   inside it look off-center. display:block removes it. The 18px circle
   (up from 17px) also makes the (container − icon) remainder an even
   number, so the centering math is a clean 4px both sides with no
   fractional-pixel rounding for the browser to get inconsistent about. */
.search-clear svg{display:block}
.search-clear:hover{color:var(--text-primary);background:var(--pill-on-border)}
.lib-search.has-text .search-clear{display:flex}

/* ── GRID CROSSFADE (pill filter change) ──────────────────── */
/* Old cards fade out before the new set cascades in. */
.grid.grid-fading{opacity:0;transition:opacity .15s ease}

/* ── MIXER MICRO-POLISH (Increment 4) ─────────────────────── */
/* Session-switcher / key dropdowns grow in from their top-right anchor. */
.dd{transform-origin:top right;animation:ddIn .16s cubic-bezier(.34,1.2,.64,1)}
@keyframes ddIn{from{opacity:0;transform:scale(.97) translateY(-6px)}to{opacity:1;transform:none}}
.dd.dd-closing{animation:ddOut .12s ease forwards;pointer-events:none}
@keyframes ddOut{from{opacity:1;transform:none}to{opacity:0;transform:scale(.97) translateY(-6px)}}

/* Entering Manage mode: the strips smoothly open a top control band, and the
   grip / ✕ controls fade in (no fill, so the ✕ hover-scale isn't clobbered).
   Timing/easing matches .mng-panel's .28s ease-out so the strips and the
   toggle's fold-out panel finish settling together, not at staggered times. */
.strip{transition:padding-top .28s ease-out,border-color .28s ease-out}
.strips{transition:gap .28s ease-out}
.console.managing .strip-grip,
.console.managing .strip-del{animation:mngCtrlIn .28s ease-out}
@keyframes mngCtrlIn{from{opacity:0;transform:translateY(-3px)}to{opacity:1;transform:none}}

/* Tactile press feedback on the main controls (the play button already has it). */
.btn:active{transform:scale(.97)}
.kbtn:active{transform:scale(.95)}
.pill:active{transform:scale(.96)}
.mx-back:active{transform:scale(.96)}

/* ── PAGE TRANSITION (Increment 5) ────────────────────────── */
/* Sequential: old view fades out (inline transition set by router.js),
   then new view mounts and fades + rises in via .view-anim.
   No overlap — nothing needed here beyond what .view-anim already does. */
.view-host{position:relative}

/* ── REQUEST ACCESS (login) ───────────────────────────────── */
.login-request{display:block;margin:14px auto 0;background:none;border:none;color:var(--text-dim);font-family:inherit;font-size:13px;cursor:pointer;text-decoration:underline;text-underline-offset:3px;transition:color .15s}
/* sits inside .login-form (a flex column) right after the password field —
   align-self pulls it to the right without needing a row wrapper */
.login-forgot{align-self:flex-end;width:auto;margin:-8px 0 0;background:none;border:none;color:var(--text-faint);font-family:inherit;font-size:12px;cursor:pointer;text-decoration:underline;text-underline-offset:3px;transition:color .15s;padding:0}
.login-forgot:hover{color:var(--accent)}
.login-request:hover{color:var(--accent)}
.field-opt{color:var(--text-faint);font-weight:400}
/* request-access form: space the stacked fields (it doesn't use .modal-fields) */
#ar-form{display:flex;flex-direction:column;gap:16px}
/* native form controls (e.g. the <select> dropdown popup) follow the active
   theme, so options stay readable in dark mode */
[data-mode="light"]{color-scheme:light}
[data-mode="dark"]{color-scheme:dark}
textarea.input{resize:vertical;min-height:48px;line-height:1.4}
select.input{cursor:pointer}

/* ── PENDING REQUESTS (accounts) ──────────────────────────── */
.acct-requests{margin-bottom:30px}
.acct-req-head{display:flex;align-items:center;gap:10px;margin:0 0 14px}
.acct-req-head h3{margin:0;font-size:16px;color:var(--text-primary)}
.req-badge{display:inline-flex;align-items:center;justify-content:center;min-width:20px;height:20px;padding:0 6px;border-radius:999px;background:var(--accent);color:#fff;font-size:12px;font-weight:600}
.req-list{display:flex;flex-direction:column;gap:10px}
.req-card{display:flex;align-items:center;justify-content:space-between;gap:16px;padding:14px 18px;border:1px solid var(--accent-border);border-radius:14px;background:var(--accent-bg)}
.req-info{min-width:0}
.req-name{font-size:14px;font-weight:600;color:var(--text-primary)}
.req-user{font-weight:400;color:var(--text-dim)}
.req-meta{font-size:12.5px;color:var(--text-dim);margin-top:3px}
.req-role{text-transform:capitalize;color:var(--accent-deep);font-weight:500}
.req-actions{display:flex;gap:8px;flex:0 0 auto}

/* pending-count badge on the Accounts nav link */
.nav-badge{display:inline-flex;align-items:center;justify-content:center;min-width:17px;height:17px;padding:0 5px;margin-left:6px;border-radius:999px;background:var(--accent);color:#fff;font-size:11px;font-weight:600;vertical-align:middle}

/* a little breathing room above an error inside a modal */
.modal .login-err.show{margin-top:12px}

/* accounts list refresh — quick fade so add/edit/remove settles smoothly */
.list-fade{animation:listFade .2s ease}
@keyframes listFade{from{opacity:.35}to{opacity:1}}

/* ── CHANGE LOG: collapsible day groups ───────────────────── */
.log-day{border:1px solid var(--card-border);border-radius:12px;background:var(--card-bg);margin-bottom:10px;overflow:hidden}
.log-day-head{display:flex;align-items:center;gap:12px;width:100%;text-align:left;
  padding:14px 18px;background:none;border:none;cursor:pointer;font-family:inherit;
  color:var(--text-primary);transition:background .12s}
.log-day-head:hover{background:var(--pill-on-bg)}
.ldh-chevron{color:var(--text-dim);font-size:18px;line-height:1;transition:transform .18s ease;display:inline-block}
.log-day.open .ldh-chevron{transform:rotate(90deg)}
.ldh-date{font-weight:600;font-size:14px}
.ldh-count{margin-left:auto;font-size:11.5px;color:var(--text-dim)}
.log-day-body{display:none;border-top:1px solid var(--card-border)}
.log-day.open .log-day-body{display:block;animation:viewIn .2s ease}
.log-day-body .tbl{margin:0}

/* sync button (admin) — styled as a pill; just needs a disabled/working state */
.sync-pill:disabled{opacity:.6;cursor:progress}

/* ── SONG DUPLICATE SUGGESTIONS (add-song modal) ──────────── */
/* Surfaced under the song-name field as the admin types. Clicking a row jumps
   straight to adding a session to that existing song. */
.song-suggest{display:none;margin-top:8px;border:1px solid var(--card-border);border-radius:10px;overflow:hidden;background:var(--card-bg)}
.song-suggest.show{display:block;animation:viewIn .18s ease}
.song-suggest-head{font-size:11px;font-weight:600;letter-spacing:.04em;text-transform:uppercase;color:var(--text-dim);padding:9px 12px 5px}
.song-suggest-item{display:flex;align-items:center;justify-content:space-between;gap:12px;width:100%;text-align:left;padding:9px 12px;background:none;border:none;border-top:1px solid var(--card-border);cursor:pointer;font-family:inherit;color:var(--text-primary);transition:background .12s}
.song-suggest-item:hover{background:var(--pill-on-bg)}
.song-suggest-item.exact{background:var(--accent-bg)}

/* ---- Planning Center import modal (Stage 2, Step 3) ----
   Themed with the PCO palette (green/charcoal/grey) throughout, not the
   app's own purple-tinted neutrals — this whole modal is Planning Center
   content, and should read as such. */
/* The toolbar trigger button — same pill shape/size as "Sync stems" right
   next to it, but PCO green instead of neutral, so it visually announces
   itself as a connection to an outside service before it's even clicked. */
.pill.pco-pill{background:var(--pco-green-bg);border-color:var(--pco-green-border);color:var(--pco-green)}
.pill.pco-pill:hover{background:var(--pco-green-border);border-color:var(--pco-green);color:var(--pco-green)}
/* The modal itself — a visible top accent bar and green-tinted border, so
   the whole surface (not just its content) reads as "you're now in a
   Planning Center flow," distinct from every other modal in this app. */
/* Wider than the 440px default. The import flow's densest screen is a row of
   song cards, each carrying a checkbox, a title, a key-detection note and a key
   picker side by side — at 440px that row had nowhere to go. */
.modal.pco-modal{max-width:600px;border-color:var(--pco-green-border);border-top:3px solid var(--pco-green)}
.modal.pco-modal .modal-actions .btn.ghost{border-color:var(--pco-green-border)}
.modal.pco-modal .modal-actions .btn.ghost:hover{background:var(--pco-green-bg);color:var(--pco-charcoal)}
/* ---- import stepper (renderSteps() in ui-library.js) --------------------
   These rules were simply missing: the markup has always been generated
   (.pco-steps > .pco-step + .pco-step-line, with .on / .done tiers), but
   nothing in this stylesheet ever matched it, so all four steps rendered as
   bare stacked text — the number, then the label, on separate lines, with no
   dots and no connecting rule.
   Layout is a single flex row: each step is a dot above its label, with a
   flexible line between consecutive steps that fills whatever space is left.
   The line's margin-top is half the dot's height, so it meets the dots on
   their centre line rather than the labels. */
/* Capped and centred rather than stretched to the modal's full width: the
   connectors are the only elastic part, so without this the extra width from
   .pco-modal above would go entirely into making them longer. */
.pco-steps{display:flex;align-items:flex-start;max-width:430px;margin:2px auto 20px}
/* The CURRENT step's dot is scale(1.14) with a 3px ring, so it paints
   1.54px (half the growth) + 3.42px (the ring, scaled with it) = 4.96px
   ABOVE its own layout box. .modal-body carries overflow-y:auto and
   therefore clips at its top edge, and .pco-steps sat just 2px below that
   edge — so the ring and the top of the circle were sliced off flat.
   The 5px is taken off this modal's title margin and handed straight back
   to the stepper's own, so every element stays exactly where it was on
   screen; the only thing that changes is that the clip edge is now 7px
   above the dot instead of 2px. Scoped with :has() to the one dialog that
   actually has a stepper — ui-admin.js and ui-mixer.js also add .pco-modal
   and must keep the standard 10px title gap. A browser without :has()
   simply keeps today's rendering rather than getting a half-applied pair. */
.modal.pco-modal:has(.pco-steps) h3{margin-bottom:5px}
.modal.pco-modal:has(.pco-steps) .pco-steps{margin-top:7px}
/* flex-basis is the CIRCLE's width, not the label's. This is the whole trick:
   sized to content, a step column is as wide as its longest child — so
   "Service type" made column 1 far wider than the 22px dot inside it, the
   connector after it started from that wide edge, and the connector next to a
   short label like "Plan" started almost touching its dot. Every gap was a
   function of label length. Pinning each column to the dot makes the four dots
   evenly spaced and every connector gap identical by construction.
   The label still sits in normal flow (so it reserves its own height) but,
   being wider than its 22px column, simply overflows it — centred, because
   align-items does that for an overflowing item too. Nothing clips it: the
   widest overhang is a few px into the modal's 28px padding. */
.pco-step{
  /* min-width:0 is load-bearing, not defensive. A flex item's default
     min-width is `auto`, which resolves to its MIN-CONTENT width — and with
     white-space:nowrap below, the min-content width of this column is the full
     width of its label. So `flex:0 0 22px` set the basis to the dot's width and
     was then silently overridden back up to the label's width, leaving the
     columns label-sized exactly as before and the connector gaps still varying
     with label length. Same trap as .pco-song-mid further down. */
  flex:0 0 22px;min-width:0;
  display:flex;flex-direction:column;align-items:center;gap:7px;
  font-size:9px;font-weight:700;letter-spacing:.03em;text-transform:uppercase;
  white-space:nowrap;color:var(--text-faint);
  transition:color .25s ease;
}
/* display:grid + place-items:center stacks the number and the tick in one cell
   so they can cross-fade in place. The springy transform curve is what gives
   the dot its small pop as it becomes the current step. */
.pco-step-n{
  width:22px;height:22px;border-radius:50%;
  display:grid;place-items:center;
  font-size:10.5px;font-weight:700;letter-spacing:0;line-height:1;
  background:var(--pill-bg);border:1px solid var(--pill-border);color:var(--text-dim);
  transition:background .25s ease,border-color .25s ease,color .25s ease,
             box-shadow .25s ease,transform .3s cubic-bezier(.34,1.45,.64,1);
}
/* Both faces of a dot occupy the same grid cell (built up front by
   renderSteps) and swap by opacity, so nothing is created or destroyed
   mid-transition — an element that didn't exist a frame ago has no previous
   state to animate from, which is why this stepper used to snap. */
.psn-num,.psn-tick{
  grid-area:1/1;
  transition:opacity .2s ease,transform .26s cubic-bezier(.34,1.45,.64,1);
}
/* tabular-nums so every digit takes the same advance width — "1" is
   noticeably narrower than the other digits proportionally, which left it
   sitting off-centre in its circle. (Measured from the font: with tabular
   figures every digit's ink sits within 0.1px of its advance centre, so
   horizontal centring needs nothing further.)
   There is deliberately NO vertical nudge. Inter is drawn so that
   ascender - descender = cap height exactly (1984 - 494 = 1490 units), which
   means a cap-height glyph is centred in its own line box BY CONSTRUCTION at
   line-height:1 — place-items:center then lands it dead centre in the dot
   with no correction at all. The translateY(.5px) that used to be here was a
   hand-tuned optical guess whose stated premise (that the line box reserves
   unused descender space) is the opposite of what this font does, and it
   pushed every digit half a pixel low. What residual drift remains is the
   browser rounding the font's ascent/descent to whole pixels at this
   fractional 10.5px size, and it differs per platform — so no fixed magic
   number can be right everywhere, and 0 is both the principled value and the
   best fit across the platforms this was measured on.
   scale(1) rather than none purely so the .done state below interpolates
   from an explicit transform, matching how .psn-tick is written. The tick is
   an SVG for the reason it always was — no baseline to fight. */
.psn-num{font-variant-numeric:tabular-nums;transform:scale(1)}
.psn-tick{display:block;opacity:0;transform:scale(.5)}
.pco-step.done .psn-num{opacity:0;transform:scale(.5)}
.pco-step.done .psn-tick{opacity:1;transform:scale(1)}
.pco-step.on .pco-step-n{transform:scale(1.14)}
/* 5px holds the connector off the dots without reading as a gap. The fill is a
   child element rather than a background swap so it can sweep: scaleX from the
   left edge going forward, and the same transition retracting right-to-left on
   Back, which is what makes the direction of travel legible. margin-top is half
   the dot height less half the rule's, so it meets them on their centre line. */
.pco-step-line{
  flex:1 1 auto;min-width:10px;height:2px;border-radius:2px;margin:10px 5px 0;
  background:var(--pill-border);overflow:hidden;
}
.pco-step-line i{
  display:block;width:100%;height:100%;background:var(--pco-green-border);
  transform:scaleX(0);transform-origin:left center;
  transition:transform .34s cubic-bezier(.4,0,.2,1);
}
.pco-step-line.done i{transform:scaleX(1)}
/* current step */
.pco-step.on{color:var(--pco-green)}
.pco-step.on .pco-step-n{
  background:var(--pco-green);border-color:var(--pco-green);color:#fff;
  box-shadow:0 0 0 3px var(--pco-green-bg);
}
/* completed steps — filled tint + the tick renderSteps() substitutes for the number */
.pco-step.done{color:var(--pco-grey)}
.pco-step.done .pco-step-n{
  background:var(--pco-green-bg);border-color:var(--pco-green-border);color:var(--pco-green);
}
/* Labels no longer drive column width, so they can't push the row out of the
   modal any more — the only remaining risk on a narrow phone is two long
   labels overhanging far enough to meet in the middle. A step down in size is
   enough insurance; the labels stay readable rather than disappearing. */
@media (max-width:430px){
  .pco-step{font-size:8px;letter-spacing:.02em}
  .pco-step-line{margin-left:4px;margin-right:4px}
}
/* ---- preview screen: header, summary stats, song rows ---------------------
   Everything in this block styles markup that has been generated all along but
   had no rules matching it at all — .pco-body-frame, .pco-plan-subtitle,
   .pco-stat-row, .pco-stat, .ps-num, .ps-lab and .pco-song-mid. The song row
   is the one that looked most broken: its markup is a three-column flex row
   (checkbox | title + key note | key picker), but .pco-song-card carried no
   display:flex, so all three stacked as blocks. The stylesheet still described
   an older two-row shape via .pco-song-top / .pco-song-bottom, classes the
   markup stopped emitting — those are removed below rather than left to look
   authoritative. */
.pco-body-frame{position:relative}
.pco-plan-subtitle{font-size:13.5px;font-weight:600;color:var(--pco-charcoal);margin:0 0 14px}

.pco-stat-row{display:flex;gap:10px;margin:0 0 16px}
.pco-stat{
  flex:1 1 0;min-width:0;
  display:flex;flex-direction:column;align-items:center;gap:3px;
  padding:11px 8px;border-radius:10px;
  background:var(--pco-green-bg);border:1px solid var(--pco-green-border);
}
.ps-num{font-size:21px;font-weight:700;line-height:1;color:var(--pco-green)}
.ps-lab{
  font-size:10px;font-weight:700;letter-spacing:.04em;text-transform:uppercase;
  color:var(--pco-grey);text-align:center;line-height:1.3;
}
/* Only when something actually needs attention — a zero here stays green so
   the row reads as "all clear" at a glance instead of always showing a warning. */
.pco-stat.warn{background:var(--danger-bg);border-color:var(--danger-border)}
.pco-stat.warn .ps-num{color:var(--danger)}

.pco-import-loading,.pco-import-empty{padding:32px 12px;text-align:center;color:var(--pco-grey);font-size:13px}
/* The team roster dropdown (ui-mixer.js) — same green-accented treatment
   as the import modal, so this reads as the same "Planning Center" family
   of surface even though it's a small anchored popup, not a modal. */
.dd.pco-dd{border-color:var(--pco-green-border)}
.roster-me-card{border:1px solid var(--pco-green-border);border-radius:12px;padding:12px 14px;margin-bottom:10px;background:linear-gradient(120deg, var(--pco-green-bg), transparent 75%);box-shadow:0 2px 10px var(--card-shadow)}
.roster-me-label{font-size:12.5px;font-weight:700;color:var(--pco-charcoal);margin-bottom:8px}
.roster-me-note{font-size:11px;color:var(--pco-grey);margin-top:7px;line-height:1.4}
/* A real, consistent two-button toggle — always both buttons, same shape
   regardless of status, so the card never changes size/layout based on
   state. Whichever button matches the CURRENT status is plain/disabled
   (not colored — a colored-but-unclickable button would still look like
   it wants clicking); the other keeps its own distinct color and stays
   genuinely actionable. */
.roster-me-toggle{display:flex;gap:8px}
.roster-toggle-btn{flex:1 1 0;display:flex;align-items:center;justify-content:center;gap:5px;height:34px;border-radius:9px;font-size:12px;font-weight:600;font-family:inherit;cursor:pointer;transition:background .15s,color .15s;border:1px solid var(--btn-border);background:var(--btn-bg);color:var(--text-faint)}
.roster-toggle-btn:disabled{cursor:default;opacity:.65}
.roster-toggle-btn.action-accept{background:var(--pco-green-bg);border-color:var(--pco-green-border);color:var(--pco-green)}
.roster-toggle-btn.action-accept:hover{background:var(--pco-green);color:#fff}
.roster-toggle-btn.action-decline{background:var(--danger-bg);border-color:transparent;color:var(--danger)}
.roster-toggle-btn.action-decline:hover{background:var(--danger);color:#fff}
.dd.pco-dd .dd-title{color:var(--pco-green)}
.roster-list{display:flex;flex-direction:column;gap:1px}
.roster-row{display:flex;align-items:center;justify-content:space-between;gap:10px;padding:8px 10px;border-radius:8px}
.roster-row-confirmed:hover{background:var(--pco-green-bg)}
.roster-row-pending:hover{background:rgba(199,154,68,.15)}
.roster-row-declined:hover{background:var(--danger-bg)}
/* Standalone Team button — no .sw-pill wrapper around it (that container's
   padding/divider rules are built for a label-block + action pairing; a
   lone button doesn't need that shape, and forcing it in was what created
   the empty vertical-bar-then-dead-space artifact). Height matches the
   other header pills (52px) directly, for alignment, rather than
   inheriting the smaller default .btn sizing. */
.team-btn{height:52px;padding:0 22px;display:inline-flex;align-items:center;justify-content:center}
.roster-team-label{padding:10px 10px 4px;font-size:10px;letter-spacing:.06em;text-transform:uppercase;color:var(--pco-green);font-weight:700}
.roster-team-label:first-child{padding-top:2px}
.roster-name{font-size:12.5px;color:var(--text-primary)}
.roster-pos{font-size:10.5px;color:var(--text-faint);font-weight:400}
.roster-you{font-size:10.5px;color:var(--pco-green);font-weight:600}
.roster-status{flex:0 0 auto;font-size:9.5px;font-weight:700;text-transform:uppercase;letter-spacing:.03em;padding:2px 7px;border-radius:999px;white-space:nowrap}
.roster-status-confirmed{background:var(--pco-green-bg);color:var(--pco-green)}
.roster-status-pending{background:rgba(199,154,68,.15);color:var(--vu-amber)}
.roster-status-declined{background:var(--danger-bg);color:var(--danger)}
.pco-import-empty p{margin:0 0 6px}
.pco-pick-list{border:1px solid var(--pco-green-border);border-radius:10px;overflow:hidden;background:var(--card-bg);max-height:320px;overflow-y:auto}
.pco-pick-row{display:block;width:100%;text-align:left;padding:12px 14px;background:none;border:none;border-top:1px solid var(--pco-green-border);cursor:pointer;font-family:inherit;font-size:13.5px;color:var(--pco-charcoal);transition:background .12s}
.pco-pick-row:first-child{border-top:none}
.pco-pick-row:hover{background:var(--pco-green-bg)}
.pco-plan-title{display:block;font-weight:600}
.pco-plan-meta{display:block;font-size:11.5px;color:var(--pco-grey);margin-top:3px}
/* Folder group labels — small caps, PCO green, matching how a folder name
   reads as metadata/context rather than a selectable item itself. */
.pco-folder-label{padding:10px 14px 5px;font-size:10.5px;font-weight:700;text-transform:uppercase;letter-spacing:.05em;color:var(--pco-green);border-top:1px solid var(--pco-green-border)}
.pco-folder-label:first-child{border-top:none}
/* Upcoming/past scope toggle, above the plans list. */
.pco-scope-row{display:flex;align-items:baseline;justify-content:space-between;gap:12px;margin-bottom:12px}
.pco-scope-toggle{flex:0 0 auto;background:none;border:none;font-family:inherit;font-size:12px;font-weight:600;color:var(--pco-green);cursor:pointer;text-decoration:underline;text-underline-offset:2px;padding:0;white-space:nowrap}
.pco-scope-toggle:hover{color:var(--pco-green-hover)}
/* Song review cards — a two-line structure (title, then a clearly labeled
   key row) rather than one single-line flex row, so nothing depends on
   width arithmetic working out for the title to stay legible. */
.pco-song-list{display:flex;flex-direction:column;gap:10px;max-height:380px;overflow-y:auto;padding:2px 4px 2px 2px}
.pco-song-card{
  display:flex;align-items:center;gap:13px;
  border:1px solid var(--pco-green-border);border-radius:10px;
  background:var(--card-bg);padding:12px 14px;
}
.pco-song-include{flex:0 0 auto;width:16px;height:16px;accent-color:var(--pco-green);cursor:pointer}
/* Middle column takes the slack. min-width:0 is what actually lets the title
   ellipsis work — without it a flex item refuses to shrink below its content
   and a long song name pushes the key picker off the card instead. */
.pco-song-mid{flex:1 1 auto;min-width:0;display:flex;flex-direction:column;gap:3px}
.pco-song-title{font-size:14px;font-weight:700;color:var(--pco-charcoal);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
/* select.pco-song-key, not .pco-song-key: it also carries .input, and
   element+class beats class alone regardless of source order. */
select.pco-song-key{flex:0 0 auto;width:104px;padding:7px 28px 7px 10px;font-size:12.5px;border-radius:8px}
.pco-song-detected{display:inline-flex;align-items:center;gap:4px;font-size:11px;color:var(--pco-green);font-weight:600}
.pco-song-detected svg{flex:0 0 auto}
.pco-song-notdetected{font-size:11px;color:var(--pco-grey)}
.pco-import-result p{margin:0 0 4px;font-size:13.5px;color:var(--pco-charcoal)}
.pco-result-list{list-style:none;padding:0;margin:10px 0 0;display:flex;flex-direction:column;gap:6px;font-size:13px;color:var(--pco-grey)}
.ssi-name{font-weight:500;font-size:13px}
.ssi-meta{font-size:11px;color:var(--text-dim);white-space:nowrap}

/* ============================================================================
   PHASE 4 — stem download, EQ/reverb FX panel, mix pill (snapshots/load-from/
   default), admin settings slider. Appended as a new section rather than
   interleaved with the rules above, so this whole block's origin is obvious
   at a glance; nothing above this line was touched.
   ============================================================================ */

/* ── mx-top layout: mix pill + session-switcher pill grouped together ──── */
.mx-top-right{display:flex;gap:14px;align-items:center;flex-wrap:wrap}
.mix-wrap{position:relative;display:flex;flex-direction:column;gap:9px;align-items:flex-end}
.team-wrap{position:relative;display:flex;flex-direction:column;gap:9px;align-items:flex-end}
.mx-loading{padding:60px 0;text-align:center;color:var(--text-dim);font-size:13px}

/* ── mix dropdown: snapshot rows (load + delete) ─────────────────────── */
.snap-item{display:flex;align-items:center;justify-content:space-between;gap:6px;padding:0!important}
.snap-load{flex:1 1 auto;text-align:left;background:none;border:none;font-family:inherit;font-size:inherit;color:var(--text-secondary);padding:9px 0 9px 11px;border-radius:10px 0 0 10px;cursor:pointer;transition:background .12s,color .12s}
.snap-load:hover{background:var(--link-hover-bg);color:var(--text-primary)}
.snap-del{flex:0 0 auto;width:22px;height:22px;margin:6px 8px 6px 0;border-radius:50%;border:1px solid var(--danger-border);background:var(--danger-bg);color:var(--danger);font-size:10px;line-height:1;cursor:pointer;display:flex;align-items:center;justify-content:center;padding:0;transition:background .15s,color .15s}
.snap-del:hover{background:var(--danger);color:#fff}

/* ── strip FX row: FX button + download button ───────────────────────── */
/* Width matches M+S's combined footprint exactly (36+6+36, see .ms and
   .ms-row below) rather than stretching to the full strip width — so
   the whole FX/M/S cluster reads as one aligned unit instead of a wide
   bar sitting above two small, unrelated-looking squares. */
.fx-row{display:flex;width:100%;justify-content:center}
.fx-row .fx-btn{width:78px;flex:0 0 auto}
.fx-btn{padding:5px 14px!important}
.fx-btn.active{background:var(--accent-bg);border-color:var(--accent-border);color:var(--accent);font-weight:600}
.dl-btn{padding:5px 9px!important;line-height:1}

/* ── download-format popover (unchanged — still used by the strip's ⬇) ── */
.dl-dd{min-width:220px}

/* ── Admin Settings (owner only) ──────────────────────────────────────── */
.settings-card{padding:30px 32px}
.settings-card h3{margin:0 0 8px;font-family:'Cormorant Garamond',serif;font-size:22px;font-weight:600;color:var(--text-primary)}
.settings-slider-wrap{margin-top:22px;max-width:420px}
/* THE TICK-CENTERING FIX. The old rule was just `position:relative` — no
 * declared height — so its rendered height came entirely from its one child,
 * the <input type=range>. That is the actual bug: a range input's rendered
 * box height, once -webkit-appearance/appearance:none is applied, is NOT
 * reliably "exactly whatever height: you set" across browsers unless margin,
 * padding and border are ALSO reset to zero and box-sizing is pinned. Nothing
 * here reset any of those, so the wrap's true height depended on the
 * browser's own default input padding/border — a few extra pixels that
 * differ by browser and OS, silently shifting where "50% of the wrap" landed
 * relative to where the 5px track line actually rendered. .settings-tick's
 * top:0 (inside a container already anchored to that same top:50%) was
 * always aiming at the CORRECT point on paper; the point itself was wrong,
 * because the box it was 50% of was an emergent height nobody had pinned down.
 *
 * Fixed by making the wrap's height AUTHORITATIVE instead of emergent: an
 * explicit 20px (matching the thumb, the largest thing that has to fit) plus
 * flex centering, so the input is centered inside a box we control — not one
 * the browser is left to infer. margin/padding/border are reset to 0 and
 * box-sizing pinned to border-box on the input itself, so "height:5px" means
 * exactly what it says with nothing else able to add invisible height on top
 * of it. Once that's true, the input's 5px box, flex-centered in the 20px
 * wrap, the thumb (also 20px, browsers center a thumb on its track by
 * default), and the ticks (top:50% of that same 20px wrap) all agree on one
 * shared centerline — instead of three different things each computing
 * their own idea of "center" against a number that was never pinned down.
 *
 * DevTools check if this needs re-verifying on your end: inspect the wrap,
 * Computed tab, confirm height reads exactly 20px. If it ever doesn't, the
 * fix above is what to re-check first. */
.settings-slider-track-wrap{position:relative;height:20px;display:flex;align-items:center}
.settings-slider{-webkit-appearance:none;appearance:none;width:100%;height:5px;margin:0;padding:0;border:0;box-sizing:border-box;border-radius:999px;background:var(--fader-track);outline:none;cursor:pointer;display:block}
.settings-slider-ticks{position:absolute;top:50%;left:0;right:0;height:0;pointer-events:none}
.settings-tick{position:absolute;top:0;width:7px;height:7px;border-radius:50%;background:var(--bg);border:1.5px solid var(--strip-border);transform:translate(-50%,-50%);transition:background .15s,border-color .15s}
.settings-tick.on{background:var(--accent);border-color:var(--accent)}
.settings-slider-labels{position:relative;height:16px;margin-top:10px;font-size:11.5px;color:var(--text-faint)}
.settings-slider-labels span{position:absolute;transform:translateX(-50%);white-space:nowrap;transition:color .15s,font-weight .15s}
.settings-slider-labels span.on{color:var(--accent);font-weight:600}
.settings-slider::-webkit-slider-thumb{-webkit-appearance:none;appearance:none;width:20px;height:20px;border-radius:50%;background:var(--accent);border:2px solid var(--bg);box-shadow:0 2px 6px rgba(0,0,0,.25);cursor:grab}
.settings-slider::-moz-range-thumb{width:20px;height:20px;border-radius:50%;background:var(--accent);border:2px solid var(--bg);box-shadow:0 2px 6px rgba(0,0,0,.25);cursor:grab}

/* ============================================================================
   PHASE 4b — Studio view: the digital/visual counterpart to the fader
   console. Mix/Studio pill, live EQ analyzer with a real (not simulated)
   curve + spectrum, X32-style band boxes, and the plugin accordion
   (Dynamics/Reverb/Pan/Drive/Phase). The earlier FX popover and its small
   knob variant are gone — replaced entirely by this. Generously sized
   throughout per explicit feedback that the popover-era controls felt
   cramped.
   ============================================================================ */

/* ── Mix / Studio pill toggle ─────────────────────────────────────────── */
.view-pill{display:inline-flex;align-items:center;height:52px;box-sizing:border-box;background:var(--pill-bg,var(--card-bg));border:1px solid var(--strip-border);border-radius:16px;padding:4px;gap:2px;position:relative}
.vp-indicator{position:absolute;top:4px;bottom:4px;left:0;width:0;border-radius:12px;background:var(--accent);transition:transform .22s cubic-bezier(.34,1.15,.64,1),width .22s cubic-bezier(.34,1.15,.64,1);z-index:0}
.vp-btn{position:relative;z-index:1}
#console,#studio{transition:opacity .13s ease,transform .13s ease}
.view-fade-out{opacity:0;transform:translateY(4px)}
.view-fade-in{opacity:0;transform:translateY(-4px)}
.vp-btn{border:none;background:transparent;border-radius:12px;padding:0 18px;height:100%;font-size:13px;font-weight:500;color:var(--text-dim);cursor:pointer;transition:background .15s,color .15s;display:flex;align-items:center}
.vp-btn.on{background:transparent;color:var(--accent-ink,#fff);transition:color .12s ease .06s}
.vp-btn:not(.on):hover{background:var(--pill-on-bg)}

/* ── Studio layout ────────────────────────────────────────────────────── */
.studio{margin-top:22px;border-radius:22px;border:1px solid var(--strip-border);background:var(--strip-bg);backdrop-filter:blur(20px);-webkit-backdrop-filter:blur(20px);box-shadow:0 4px 30px var(--card-shadow);padding:22px 16px;transition:background .3s,border-color .3s}
.studio-stems{display:flex;gap:8px;flex-wrap:wrap;margin-bottom:18px}
.studio-stem-tab{border:1px solid var(--strip-border);background:var(--card-bg);border-radius:999px;padding:9px 20px;font-size:13.5px;font-weight:500;color:var(--text-dim);cursor:pointer;transition:background .15s,color .15s,border-color .15s}
.studio-stem-tab.on{background:var(--accent);border-color:var(--accent);color:var(--accent-ink,#fff)}
.studio-stem-tab:not(.on):hover{border-color:var(--accent-border)}

.studio-body{display:grid;grid-template-columns:1fr 420px;gap:16px;align-items:start}
@media (max-width:980px){ .studio-body{grid-template-columns:1fr} }

/* ── EQ card: a "labeled console section" — colored top edge + a tinted
   header bar naming it, mimicking a physical console's printed section
   label (item: colored console-section framing). EQ reuses --accent
   directly rather than a new color — the curve and dots below are already
   drawn in --accent, so the label just names an association that already
   exists instead of competing with it. No overflow:hidden on this card:
   the band-type dropdown (below) is nested inside it and pops open past
   the card's own bottom edge — clipping the card would silently clip the
   dropdown. The header's rounded top corners are cut with an explicit
   border-radius instead, so nothing needs to rely on overflow to look
   right. */
.studio-eq-card{background:var(--card-bg);border:1px solid var(--strip-border);border-radius:16px}
.studio-eq-head{display:flex;align-items:center;justify-content:space-between;gap:14px;padding:14px 20px;border-bottom:1px solid var(--strip-border)}
.studio-eq-head-left{display:flex;align-items:center;gap:10px;min-width:0}
.studio-section-tag{flex:0 0 auto;font-size:10.5px;font-weight:700;letter-spacing:.07em;text-transform:uppercase;color:var(--text-faint);background:var(--strip-bg);border:1px solid var(--strip-border);border-radius:5px;padding:2px 7px}
.studio-section-tag.fx{color:var(--text-faint)}
.studio-eq-title{font-size:14.5px;font-weight:500;color:var(--text-primary);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.studio-phase-btn{width:38px;height:38px;flex:0 0 auto;border-radius:10px;border:1px solid var(--strip-border);background:var(--bg);color:var(--text-dim);cursor:pointer;display:flex;align-items:center;justify-content:center;padding:0;font-size:17px;line-height:1;transition:background .15s,color .15s,border-color .15s}
.studio-phase-btn i{font-style:normal;display:flex;align-items:center;justify-content:center;width:100%;height:100%}
.studio-phase-btn.on{background:var(--accent-bg);border-color:var(--accent-border);color:var(--accent)}
.studio-phase-btn:hover{border-color:var(--accent-border)}

.studio-eq-content{padding:22px 20px 24px}
.studio-eqsvg{width:100%;height:auto;display:block;border-radius:10px;background:var(--strip-bg,var(--bg))}
.studio-grid{stroke:var(--strip-border);stroke-width:1}
.studio-grid-0{stroke:var(--text-faint);stroke-width:1.25}
.studio-curve{fill:none;stroke:var(--accent);stroke-width:3;stroke-linejoin:round}
.studio-dot{stroke:var(--card-bg);stroke-width:2.5;cursor:grab}
.studio-dot-sel{stroke:var(--text-primary);stroke-width:3.5}
.studio-freq-axis{position:relative;height:13px;font-size:11px;color:var(--text-faint);margin:6px 2px 20px}
.studio-freq-axis span{position:absolute;white-space:nowrap}

/* ── Band readout boxes — horizontal-expansion redesign. The selected box
   widens in place to hold its own live dials; the rest compress to a
   minimal label (just its type — the band/section name moved to the tab
   above it, see .studio-box-tab), nothing that needs live updates while a
   neighbor is being dragged. Each band is a .studio-box-slot (tab + box
   stacked in a column) — the SLOT is the actual flex item now, not the
   box, since the tab needs to size independently above it.
   The width change is driven by a one-shot @keyframes animation (below),
   not a CSS transition on a static class match — that was tried first and
   never actually animated anything. The reason: renderBoxes() wipes and
   rebuilds the entire row from scratch on every selection change
   (boxesHost.innerHTML=''), so every slot is a brand-new element each
   time, already carrying its final classes the instant it's created. A
   *transition* needs a previously-rendered frame at the old value to
   interpolate away from — a freshly-inserted element has none, so it just
   appears already in its end state; that's what looked like "jump cuts,
   no fade in or out" rather than jitter. A CSS *animation* doesn't have
   that requirement — it defines its own from/to keyframes and plays them
   regardless of what the element looked like a moment ago, which is
   exactly what's needed here. This is a DIFFERENT bug from the earlier
   transform/FLIP attempt (which animated fine but painted over neighbors
   since transform doesn't reserve layout space) — this keeps flex-basis
   itself as the animated property, so space is genuinely reserved
   throughout, same as the transition version was supposed to provide, it
   just never played. flex-wrap is the narrow-viewport fallback — a real
   responsive pass is Phase 6 (Mobile/PWA) work, not this one. */
.studio-boxes{display:flex;flex-wrap:wrap;align-items:stretch;gap:8px;margin-bottom:18px}
.studio-box-slot{
  display:flex;flex-direction:column;
  flex:0 0 76px;contain:layout;will-change:flex-basis;
}
.studio-box-slot:has(.studio-box.selected){
  flex:1 1 320px;min-width:320px;
}
.studio-box-slot.studio-box-slot-expanding{
  animation:boxSlotExpand .26s cubic-bezier(.4,0,.2,1) both;
}
.studio-box-slot.studio-box-slot-collapsing{
  animation:boxSlotCollapse .26s cubic-bezier(.4,0,.2,1) both;
}
@keyframes boxSlotExpand{
  from{ flex-grow:0; flex-shrink:0; flex-basis:76px; min-width:76px; }
  to{ flex-grow:1; flex-shrink:1; flex-basis:320px; min-width:320px; }
}
@keyframes boxSlotCollapse{
  from{ flex-grow:1; flex-shrink:1; flex-basis:320px; min-width:320px; }
  to{ flex-grow:0; flex-shrink:0; flex-basis:76px; min-width:76px; }
}
/* the tab: a small pill attached to the box's top edge, inset from its
   left corner like a manila folder tab, carrying the "Band N"/"Low Cut"
   label that used to sit inside the box itself. No gap between tab and
   box (flex-direction:column, default 0 gap) — they're meant to read as
   one connected shape, the box's own top border continuing on either
   side of the tab. */
.studio-box-tab{
  align-self:flex-start;flex:0 0 auto;
  margin-left:10px;padding:4px 10px 5px;
  font-size:10px;font-weight:700;letter-spacing:.02em;white-space:nowrap;
  color:var(--box-color);background:var(--bg);
  border:1px solid var(--strip-border);border-bottom:none;
  border-radius:8px 8px 0 0;
}
.studio-box{
  --box-color:var(--accent);
  flex:1 1 auto;display:flex;flex-direction:column;justify-content:center;
  text-align:left;background:var(--bg);
  border:1.5px solid var(--strip-border);border-radius:12px;padding:12px 14px;
  cursor:pointer;overflow:hidden;transition:border-color .15s,background .15s;
}
.studio-box:not(.selected){align-items:flex-start}
.studio-box:hover{border-color:var(--box-color)}
.studio-box.selected{cursor:default;border-color:var(--box-color);background:var(--card-bg)}
.studio-box.studio-box-off{opacity:.55}
/* the brief window between clicking a different box and this one actually
   swapping to its collapsed content — still showing its dials, fading
   out, not interactive (clicking a fading ghost is more confusing than
   useful; the box you actually meant to click is elsewhere). Same fix as
   above: a keyframe animation (not a transition), since this row is also
   born inside an already-.studio-box-collapsing parent, not transitioning
   into it. */
.studio-box.studio-box-collapsing{pointer-events:none}
.studio-box-collapsing .studio-dial-row{
  animation:boxContentFadeOut .15s ease-out both;
  transform-origin:left center;
}
@keyframes boxContentFadeOut{
  from{ opacity:1; transform:scale(1); }
  to{ opacity:0; transform:scale(.45); }
}
.studio-box-collapse-settle .studio-box-icon,
.studio-box-collapse-settle .studio-box-vals{animation:boxCollapseFadeIn .18s ease both}
@keyframes boxCollapseFadeIn{from{opacity:0}to{opacity:1}}
.studio-box-sub{font-size:11px;color:var(--text-faint);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.studio-box .studio-dial-row{justify-content:flex-start;align-items:center;flex-wrap:nowrap;gap:14px;padding:0;min-height:118px}
.studio-box-exp-enter{animation:boxExpIn .18s ease both}
@keyframes boxExpIn{from{opacity:0;transform:translateY(3px)}to{opacity:1;transform:none}}
.studio-lowcut-toggle{display:flex;align-items:center}

/* ── collapsed-box content: a small representative curve icon plus the
   band's actual current Freq/Gain/Q, replacing what used to be just the
   type name. ── */
.studio-box-icon{width:100%;color:var(--box-color);opacity:.85}
.studio-box-icon-svg{display:block;width:100%;height:18px}
.studio-box-vals{margin-top:6px;display:flex;flex-direction:column;gap:2px}
.studio-box-vals span{font-size:10.5px;color:var(--text-dim);font-variant-numeric:tabular-nums}

/* ── band-type picker: a 2x3 grid of short buttons filling the empty
   space next to Freq/Gain/Q, instead of a dropdown of any kind. Direct
   single-click selection — no open/close state, so there's no popup
   left to ever overlap or mis-anchor. flex:1 lets it fill whatever room
   the box has beyond the three dials; min-width is just enough for two
   columns of short labels not to crush together on a narrower box. Each
   button carries real padding rather than stretching to match the dials'
   height, so the grid's natural height comes out a bit taller than the
   dial column — combined with .studio-dial-row's align-items:center
   above, that's what centers the (shorter) dials against the grid rather
   than pinning everything to the top. Only the 4 outer corners round to
   echo the box's own shape (10px, a hair under the box's 12px since this
   sits inset from its actual edge by the box's own padding) — the inner
   corners between buttons stay small. ── */
.studio-type-grid{display:grid;grid-template-columns:1fr 1fr;grid-template-rows:repeat(3,36px);gap:5px;flex:1 1 auto;min-width:100px}
.studio-box .studio-dial-row .studio-type-grid{flex-shrink:0}
.studio-type-btn{display:flex;align-items:center;justify-content:center;background:var(--bg);border:1px solid var(--strip-border);color:var(--text-dim);font-size:11px;font-weight:500;border-radius:6px;cursor:pointer;padding:0 6px;transition:border-color .15s,color .15s}
.studio-type-btn:hover{border-color:var(--box-color);color:var(--text-primary)}
.studio-type-btn.cur{border-color:var(--box-color);color:var(--box-color);font-weight:700}
.studio-type-btn:nth-child(1){border-top-left-radius:10px}
.studio-type-btn:nth-child(2){border-top-right-radius:10px}
.studio-type-btn:nth-child(5){border-bottom-left-radius:10px}
.studio-type-btn:nth-child(6){border-bottom-right-radius:10px}

.studio-acc-item + .studio-acc-item{border-top:1px solid var(--strip-border)}
.studio-acc-head{display:flex;align-items:center;justify-content:space-between;width:100%;padding:6px 20px 6px 8px}
.studio-acc-disclose{display:flex;align-items:center;gap:10px;flex:1;background:none;border:none;color:var(--text-primary);font-size:14px;font-weight:500;padding:10px 8px;cursor:pointer;text-align:left}
.studio-acc-disclose.open .studio-acc-chev{transform:rotate(90deg)}
.studio-acc-title{pointer-events:none}
.studio-acc-meta{display:flex;align-items:center;gap:12px;flex-shrink:0}
.studio-acc-preview{font-size:12px;font-weight:400;color:var(--text-faint);min-width:2ch;text-align:right}
.studio-acc-chev{color:var(--text-faint);transition:transform .15s;display:inline-block;pointer-events:none}
.studio-acc-body-outer{display:grid;grid-template-rows:0fr;transition:grid-template-rows .22s ease}
.studio-acc-body-outer.open{grid-template-rows:1fr}
.studio-acc-body{overflow:hidden;min-height:0;padding:0 14px;transition:padding .22s ease}
.studio-acc-body-outer.open .studio-acc-body{padding:6px 20px 22px}
.studio-gr-val{font-size:10.5px;color:var(--text-faint);margin-top:4px;width:40px;text-align:center;font-variant-numeric:tabular-nums}

.studio-dial-row{display:flex;justify-content:space-around;align-items:flex-start;gap:16px;flex-wrap:wrap;padding:6px 0}

/* ── Dial: SVG-based rotary control, replaces the old FX popover knobs ── */
.dial-wrap{display:flex;flex-direction:column;align-items:center;gap:8px;transition:opacity .18s ease}
.studio-box .studio-dial-row .dial-wrap{flex-shrink:0}
.dial-wrap.disabled{opacity:.4;pointer-events:none}
.dial-lab{font-size:10.5px;letter-spacing:.06em;text-transform:uppercase;color:var(--text-faint)}
.dial{cursor:ns-resize;touch-action:none;outline:none;overflow:visible;filter:drop-shadow(0 2px 3px var(--card-shadow))}
.dial:focus-visible .dial-track{stroke:var(--accent)}
.dial-track{fill:var(--ctrl-bg);stroke:var(--ctrl-border);stroke-width:1}
.dial-sheen{pointer-events:none}
.dial-notch{stroke:var(--accent);stroke-width:2.5;stroke-linecap:round;filter:drop-shadow(0 0 3px var(--accent))}
.dial-val{font-size:12.5px;font-weight:500;color:var(--text-primary);min-height:16px}

/* ── accordion: Dynamics / Reverb / Pan / Drive / Phase ──────────────── */
/* FX/Processing card: the second "labeled console section," matching
   the EQ card above in --section-fx so the two read as a deliberate pair
   rather than one card having a header and the other not. Unlike the EQ
   card, overflow:hidden here is safe — nothing in the accordion (toggles,
   dials) ever needs to render outside this card's bounds — and it's what
   clips the reset button's bottom corners to the card's own radius. */
.studio-fx-card{background:var(--card-bg);border:1px solid var(--strip-border);border-radius:16px;overflow:hidden}
.studio-fx-head{display:flex;align-items:center;gap:10px;padding:14px 20px;border-bottom:1px solid var(--strip-border)}
.studio-fx-title{font-size:14.5px;font-weight:500;color:var(--text-primary)}
.studio-accordion{background:transparent}

.studio-gr-meter-wrap{display:flex;flex-direction:column;align-items:center;gap:8px}
.studio-gr-lab{font-size:10.5px;letter-spacing:.06em;text-transform:uppercase;color:var(--text-faint)}
.studio-gr-meter{width:16px;height:48px;background:var(--bg);border:1px solid var(--strip-border);border-radius:4px;position:relative;overflow:hidden}
.studio-gr-fill{position:absolute;bottom:0;left:0;right:0;height:0%;background:var(--danger,#d4537e);transition:height .08s linear}

.studio-reset-btn{display:block;width:100%;text-align:left;background:none;border:none;border-top:1px solid var(--strip-border);color:var(--danger,#d4537e);font-size:13px;padding:14px 20px;cursor:pointer}
.studio-reset-btn:hover{background:var(--danger-bg)}

/* ============================================================================
   PHASE 4c — feedback round: grouped/searchable mix dropdown, loop toggle,
   downloads modal, dimmed-when-off dial rows. Appended as its own section for
   the same reason as the Phase 4/4b blocks above.
   ============================================================================ */

/* ── mix dropdown: person grouping + search ──────────────────────────── */
/* Base width; grows to fit an opened person's mixes without horizontal
   scroll, capped at the viewport so it never runs off-screen — past the
   cap the .dd's own max-height/overflow takes over (vertical scroll only).
   The width transition makes the expand feel deliberate rather than a
   jump. min() cap leaves a margin on very narrow screens. */
.mix-dd{width:300px;max-width:min(440px, calc(100vw - 24px));transition:width .18s ease}
.mix-dd.has-open-person{width:min(440px, calc(100vw - 24px))}
.mix-search{width:100%;box-sizing:border-box;background:var(--bg);border:1px solid var(--strip-border);border-radius:8px;padding:8px 10px;color:var(--text-primary);font-size:12.5px;margin:2px 8px 6px}
.mix-person{margin:0 2px}
.mix-person-head{display:flex;align-items:center;justify-content:space-between;width:100%;background:none;border:none;color:var(--text-secondary);font-size:13px;padding:9px 8px;border-radius:10px;cursor:pointer}
.mix-person-head:hover{background:var(--link-hover-bg)}
.mix-person-count{display:flex;align-items:center;gap:4px;font-size:11.5px;color:var(--text-faint)}
.mix-person-chev{font-style:normal;transition:transform .15s;display:inline-block}
.mix-person-head.open .mix-person-chev{transform:rotate(90deg)}
.mix-person-body{display:none;flex-direction:column;padding-left:10px}
.mix-person-body.open{display:flex}
.mix-person-item{flex:1 1 auto;min-width:0;font-size:12.5px;color:var(--text-dim);padding:7px 8px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;text-align:left}
/* a person's mix row: the loadable mix + (admin) a trash button beside it */
.mix-person-row{display:flex;align-items:center;gap:2px}
.mix-del-other{flex:0 0 auto;background:none;border:none;color:var(--text-faint);font-size:12px;line-height:1;padding:6px 8px;border-radius:8px;cursor:pointer;transition:background .12s,color .12s}
.mix-del-other:hover{background:var(--danger-bg,rgba(220,80,80,.14));color:var(--danger,#e0645f)}
/* former (deleted-account) owners: muted, flagged, and sorted last so an
   admin can spot and clear leftover mixes */
.mix-person-former .mix-person-head{color:var(--text-faint)}
.mix-former-tag{font-style:normal;font-size:10px;font-weight:600;letter-spacing:.02em;text-transform:uppercase;color:var(--danger,#e0645f);opacity:.8;margin-left:6px}

/* ── mix pill name + marquee for long names ──────────────────────────────
   The name sits in a fixed-width, clipped slot so the pill never stretches
   to fit a long "Jonathan's (edited)". If the text overflows that slot,
   JS adds .is-marquee and the inner span scrolls its overflow back and
   forth (distance + timing set inline per-name), pausing at each end. */
.sw-mix-name{display:block;max-width:150px;overflow:hidden;white-space:nowrap}
.sw-mix-marquee{display:inline-block;white-space:nowrap}
.sw-mix-marquee.is-marquee{animation:mixMarquee var(--marquee-time,6s) ease-in-out infinite alternate}
@keyframes mixMarquee{
  0%,18%{transform:translateX(0)}
  82%,100%{transform:translateX(var(--marquee-dist,0))}
}
@media (prefers-reduced-motion: reduce){
  .sw-mix-marquee.is-marquee{animation:none;text-overflow:ellipsis;overflow:hidden;max-width:150px}
}

/* ── loop toggle (transport) ─────────────────────────────────────────── */
.loop-toggle{display:flex;align-items:center;gap:6px;border:1px solid var(--strip-border);background:var(--card-bg);color:var(--text-dim);border-radius:999px;padding:7px 14px;font-size:12.5px;cursor:pointer;transition:background .15s,color .15s,border-color .15s}
.loop-toggle i{font-style:normal;font-size:14px;line-height:1}
.loop-toggle.on{background:var(--accent-bg);border-color:var(--accent-border);color:var(--accent);font-weight:500}
.loop-toggle:hover{border-color:var(--accent-border)}

/* ── downloads modal ─────────────────────────────────────────────────── */
.dl-modal-list{display:flex;flex-direction:column;gap:2px;max-height:360px;overflow:auto}
.dl-modal-row{display:flex;align-items:center;justify-content:space-between;padding:10px 4px;border-bottom:1px solid var(--strip-border)}
.dl-modal-row:last-child{border-bottom:none}
.dl-modal-name{font-size:13.5px;color:var(--text-primary)}
.dl-modal-btns{display:flex;gap:8px}

/* ── dimmed dial row when a plugin (e.g. Dynamics) is off ────────────── */
.studio-dial-row-off{opacity:.45;transition:opacity .18s ease}

/* ── generic on/off pill toggle (checkbox-driven, unlike .toggle above which
   is hardcoded to the dark/light data-mode attribute) — used for Low Cut and
   every accordion section's bypass switch ──────────────────────────────── */
.pill-toggle{position:relative;display:inline-flex;width:34px;height:20px;flex-shrink:0;cursor:pointer}
.pill-toggle input{opacity:0;width:0;height:0;position:absolute}
.pill-toggle-track{position:absolute;inset:0;border-radius:999px;background:var(--pill-bg);border:1px solid var(--pill-border);transition:background .18s,border-color .18s}
.pill-toggle-thumb{position:absolute;top:50%;left:2px;width:14px;height:14px;border-radius:50%;background:var(--text-faint);transform:translateY(-50%);transition:transform .18s cubic-bezier(.34,1.4,.64,1),background .18s}
.pill-toggle input:checked ~ .pill-toggle-track{background:var(--accent-bg);border-color:var(--accent-border)}
.pill-toggle input:checked ~ .pill-toggle-track .pill-toggle-thumb{transform:translate(14px,-50%);background:var(--accent)}

/* ============================================================================
 * RECOVERY BLOCK — restoring CSS lost when tokens.css was accidentally
 * overwritten with a stale snapshot mid-session. Everything below was
 * verified either by extracting the last definition from this project's
 * own conversation transcripts (rail/mix-tab/files-dd/match-banner — all
 * pre-existing work), or restored directly from this session's own visible
 * history (files-modal/backfill/spinner — built after that point). Every
 * selector was cross-checked against what ui-mixer.js/ui-admin.js actually
 * reference today before being included.
 * ==========================================================================*/

/* ---- rail (Session/Key/Files/Team/Mix icon column) ---- */
.rail{width:58px;flex:0 0 auto;display:flex;flex-direction:column;gap:8px;margin-top:0}
/* Groups Team + Files together (both Planning-Center-sourced — see the
   comment on pcoGroupWrap in ui-mixer.js). Same gap as the outer .rail
   itself, so the spacing between every rail item reads as consistent
   rather than the group's two icons touching directly while everything
   else gets a real gap. */
/* Every direct child of .rail needs this too, not just .rail itself —
   .rail's own gap:8px only applies BETWEEN its direct children (the
   wrapper divs), not between icons stacked inside one of them.
   #rail-session-key holds two icons (Session, Key); #rail-pco-group
   holds two (Team, Files); #rail-mix holds one. Without this, icons
   sharing a wrapper sit flush against each other with zero gap while
   everything else gets 8px — exactly what reads as "unevenly spaced".
   A general child-combinator rather than one rule per wrapper id, so it
   automatically covers any wrapper .rail ever contains, present or
   future, not just the ones that existed when this was first written. */
.rail>div{display:flex;flex-direction:column;gap:8px}
.rail-icon{width:58px;height:58px;box-sizing:border-box;background:var(--strip-bg);border:1px solid var(--strip-border);border-radius:16px;display:flex;align-items:center;justify-content:center;cursor:pointer;position:relative;color:var(--text-secondary);transition:background .15s,border-color .15s}
.rail-icon:hover{background:var(--link-hover-bg)}
.rail-icon.key{color:var(--accent)}
.rail-icon.files{background:linear-gradient(135deg, var(--pco-green-bg), transparent 80%);border-color:var(--pco-green-border)}
.rail-icon.team{background:linear-gradient(135deg, var(--pco-green-bg), transparent 80%);border-width:2px;border-color:var(--pco-green-border)}
.rail-key-letter{font-family:'Cormorant Garamond',serif;font-style:italic;font-size:22px;font-weight:600;line-height:1}
.rail-dot{position:absolute;top:7px;right:7px;width:6px;height:6px;border-radius:50%;background:var(--pco-green)}
.rail-tip{position:absolute;right:calc(100% + 10px);top:50%;transform:translateY(-50%);background:var(--menu-bg);border:1px solid var(--menu-border);border-radius:10px;padding:8px 13px;font-size:11.5px;white-space:nowrap;opacity:0;pointer-events:none;transition:opacity .12s;display:flex;flex-direction:column;gap:1px;align-items:flex-end;box-shadow:0 12px 28px var(--menu-shadow);z-index:5}
.rail-icon:hover .rail-tip,.rail-icon:focus-visible .rail-tip{opacity:1}
.rt-lab{font-size:9px;text-transform:uppercase;letter-spacing:.05em;color:var(--text-faint)}
.rt-val{color:var(--text-primary)}
.mx-body{display:flex;gap:12px;align-items:flex-start}
.mx-main{flex:1;min-width:0}

/* ---- Mix/Studio segmented tabs ---- */
.mix-tabs{position:relative;display:flex;gap:3px;background:var(--bg);border-radius:10px;padding:3px;margin:2px 2px 8px}
.mix-tab{position:relative;flex:1;text-align:center;font-size:11.5px;padding:7px 4px;border-radius:8px;color:var(--text-dim);cursor:pointer;display:flex;align-items:center;justify-content:center;gap:4px;background:none;border:none;font-family:inherit;transition:color .18s}
.mix-tab.on{color:var(--accent);font-weight:600}
.mix-tab .mt-n{background:var(--ctrl-bg);border-radius:7px;padding:0 5px;font-size:9.5px}
.mix-tab.on .mt-n{background:var(--accent-bg)}
.mix-tab-thumb{position:absolute;top:3px;height:calc(100% - 6px);background:var(--accent-bg);border-radius:8px;transition:left .24s cubic-bezier(.34,1.15,.64,1),width .24s cubic-bezier(.34,1.15,.64,1)}
.mix-tab-panel{display:none}
.mix-tab-panel.on{display:block;animation:panelFadeIn .18s ease}
@keyframes panelFadeIn{from{opacity:0;transform:translateY(3px)}to{opacity:1;transform:translateY(0)}}

/* ---- Files dropdown rows ---- */
/* Team/roster dropdown — capped and scrollable, same as Files below, just
   wider to fit names/positions comfortably without wrapping.
   This USED to be max-height:none;overflow:visible — reasoned correctly
   for Files at the time (past FILES_MODAL_THRESHOLD, openFilesDD is never
   even called; UI.filesModal handles it instead, so the small dropdown
   itself only ever needs to hold a handful of rows) but that reasoning
   only holds as long as EVERY caller of this class is actually gated by a
   matching threshold check — and the roster dropdown never had one. A
   session with a large team (a big Sunday service, a Christmas Eve
   gathering) had no ceiling at all: no modal fallback, no scroll, nothing
   — the exact failure this whole pass exists to close, just with no
   fallback whatsoever rather than a broken one. min() here follows the
   same pattern .mix-dd already uses further down in this file for WIDTH
   (a fixed cap that also never exceeds the viewport) — same idea, just
   applied to height/100vh instead of width/100vw. Restoring this doesn't
   remove the case for a roster modal past some threshold — it's the
   backstop underneath one, not a replacement for it. */
.dd.roster-dd{max-height:min(420px, calc(100vh - 120px));overflow-y:auto;min-width:340px}
.dd.files-dd{max-height:min(420px, calc(100vh - 120px));overflow-y:auto;min-width:300px}
.file-row{text-decoration:none}
.dd.files-dd .file-row:not(:last-child){border-bottom:1px solid var(--divider)}
.file-row-name{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.file-row-badge{flex:0 0 auto;font-size:9.5px;font-weight:700;text-transform:uppercase;letter-spacing:.03em;color:var(--pco-green);background:var(--pco-green-bg);border:1px solid var(--pco-green-border);border-radius:999px;padding:2px 7px}
.file-row-type{flex:0 0 auto;color:var(--text-faint);display:flex;align-items:center}

/* ---- Stage 4 fuzzy-match banner (import + backfill review both use this) ---- */
.pco-match-banner{display:flex;flex-direction:column;gap:10px;background:var(--accent-bg);border:1px solid var(--accent-border);border-radius:10px;padding:12px 13px;font-size:12px}
.pmb-text{color:var(--text-primary)}
.pmb-candidates{display:flex;flex-direction:column;gap:6px}
.pmb-candidate-row{display:flex;align-items:center;gap:10px;width:100%;box-sizing:border-box;padding:9px 12px;border-radius:8px;border:1px solid var(--accent-border);background:var(--card-bg)}
.pmb-candidate-name{flex:1 1 auto;min-width:0;font-size:12.5px;font-weight:700;color:var(--accent);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.pmb-score{flex:0 0 auto;font-weight:700;color:var(--accent);background:var(--accent-bg);border:1px solid var(--accent-border);border-radius:999px;padding:2px 8px;font-size:10.5px}
.pmb-merge-btn{flex:0 0 auto;font-family:inherit;font-size:11.5px;font-weight:700;padding:6px 13px;border-radius:7px;cursor:pointer;border:none;background:var(--accent);color:#fff;transition:background .12s}
.pmb-inline-reject{font-family:inherit;font-size:11.5px;color:var(--text-dim);background:none;border:none;padding:0;cursor:pointer;text-decoration:underline;white-space:nowrap;transition:color .12s}
.pmb-undo{font-family:inherit;font-size:11px;color:var(--text-dim);background:none;border:none;cursor:pointer;text-decoration:underline;flex:0 0 auto;padding:0}

/* ---- Files modal (>8 files) ---- */
.files-modal-section{margin-bottom:18px}
.files-modal-section.no-header{margin-bottom:8px}
.files-modal-section-label{font-size:11px;font-weight:700;text-transform:uppercase;letter-spacing:.06em;color:var(--text-faint);margin-bottom:8px}
.files-modal-rows{display:flex;flex-direction:column}
.files-modal-rows .file-row:not(:last-child){border-bottom:1px solid var(--divider)}
.files-modal-expand{display:block;width:100%;text-align:center;font-family:inherit;font-size:11.5px;font-weight:600;color:var(--accent);background:var(--accent-bg);border:1px solid var(--accent-border);border-radius:8px;padding:8px;margin-top:6px;cursor:pointer;transition:background .12s}
.files-modal-expand:hover{background:var(--accent-border)}
.files-modal-collapsed{margin-top:6px}

/* ---- Backfill review screen ---- */
.backfill-list{display:flex;flex-direction:column;gap:8px;margin-top:14px}
.backfill-row{display:flex;flex-direction:column;gap:10px;background:var(--accent-bg);border:1px solid var(--accent-border);border-radius:10px;padding:12px 13px;font-size:12px}
.backfill-row.resolved{background:var(--card-bg);border-color:var(--pco-green-border);flex-direction:row;align-items:center;justify-content:space-between}
.backfill-row.resolved.confirmed .pmb-text{color:var(--pco-green)}
.backfill-candidate-info{flex:1 1 auto;min-width:0;display:flex;flex-direction:column;gap:2px}
.backfill-candidate-meta{font-size:10.5px;color:var(--text-faint);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}

/* ---- Files loading spinner ---- */
@keyframes railSpin{ to{ transform:rotate(360deg); } }
.rail-icon.files.loading .rail-spinner{animation:railSpin .9s linear infinite;transform-origin:center}

/* ============================================================================
   PHASE 4 CLEANUP — rules for classes the JS has always applied but that
   nothing here ever styled. Appended as a new section rather than edited into
   the sections above, matching how every previous phase extended this file.
   ==========================================================================*/

/* ---- Planning Center import: the step transition -------------------------
   js/ui-library.js animateStepTransition() has always set --pco-out and
   --pco-in on #pco-import-body and toggled these two classes around the
   content swap, and NONE of the four had a rule here. So the wizard paid a
   full ANIM_OUT_MS wait on every step change and produced no movement for it;
   only the height transition (driven by inline styles in animateFrameHeight)
   ever worked.

   ANIMATIONS, not transitions, and that distinction is load-bearing:
   .pco-anim-in is applied in the same frame the content is replaced, and a
   transition on a freshly-created element never fires because there is no
   previous value to transition FROM. An animation runs regardless.

   Durations mirror the JS constants exactly — 140ms out, 280ms in. If either
   constant changes, change it here too or the class will be removed part-way
   through its own animation.

   The prefers-reduced-motion block earlier in this file already collapses
   both of these to an instant change. */
@keyframes pcoStepOut{
  from{ opacity:1; transform:translateX(0) }
  to  { opacity:0; transform:translateX(var(--pco-out,-14px)) }
}
@keyframes pcoStepIn{
  from{ opacity:0; transform:translateX(var(--pco-in,14px)) }
  to  { opacity:1; transform:translateX(0) }
}
.pco-anim-out{ animation:pcoStepOut 140ms cubic-bezier(.4,0,1,1) both }
.pco-anim-in { animation:pcoStepIn  280ms cubic-bezier(0,0,.2,1) both }

/* ---- Roster: the fourth status ------------------------------------------
   confirmed / pending / declined each get a hover tint above; `unknown` is a
   real value normalizeStatus() returns whenever Planning Center reports no
   status or one we don't recognise, and it was the only row in the list with
   no hover feedback at all. Neutral on purpose — an unknown status shouldn't
   be tinted as though it meant something. */
.roster-row-unknown:hover{ background:var(--row-hover) }

/* ============================================================================
   SETTINGS CARD — section structure
   The Sanctuary Settings card carried three unrelated things (instrument
   vocabulary, download permission, leftover mixes) as three bare
   heading+paragraph+control stacks with nothing between them, so there was no
   visual boundary telling you where one setting ended and the next began. On a
   wide window the explanatory text also ran the full card width — around 250
   characters a line, which is roughly three times a comfortable measure.

   Metrics below deliberately match the .pco-tools family above, so the
   Planning Center card and this one read as one system instead of two designs
   stacked on each other.
   ==========================================================================*/

/* Each setting is its own block, separated by a rule rather than by guesswork
   with margins. :first-child rather than a modifier class so the top section
   needs no special markup — and so that a section which hides itself (the
   removed-accounts one does exactly that) never leaves a stray divider or a
   gap behind. */
.settings-section + .settings-section{ margin-top:26px; padding-top:26px; border-top:1px solid var(--divider) }
.settings-section > .btn{ margin-top:2px }

/* A comfortable measure. The card itself stays full width — only the prose is
   constrained, so controls and rows still use the whole space. */
.settings-card .field-hint{ max-width:78ch; line-height:1.5 }

/* Labelled sub-panel, same shape as .pco-tools. */
.settings-panel{ margin-top:14px; border:1px solid var(--card-border); border-radius:12px; padding:14px 16px; background:var(--card-bg) }
.settings-panel-label{ font-size:10px; font-weight:700; letter-spacing:.06em; text-transform:uppercase; color:var(--text-faint); margin-bottom:4px }
.settings-panel-note{ margin:0 0 4px; font-size:11.5px; line-height:1.5; color:var(--text-faint); max-width:78ch }

/* One row per item, divider-separated, mirroring .pco-tool. The trailing
   control is pinned right and never shrinks; the info block is allowed to
   shrink, and min-width:0 is what lets it — without it a flex child refuses to
   go below its content width and pushes the button off the edge. */
.settings-row{ display:flex; align-items:center; gap:14px; padding:10px 0; border-top:1px solid var(--divider) }
.settings-row:first-child{ border-top:none }
.settings-row-info{ flex:1 1 auto; min-width:0; display:flex; flex-direction:column; gap:2px }
.settings-row-title{ font-size:13px; color:var(--text-primary) }
/* Wraps. These carry the setting's explanation now, not a one-line timestamp,
   so the old nowrap+ellipsis would have truncated most of them. Measure capped
   so a wide window doesn't stretch a sentence across the whole screen. */
.settings-row-meta{ font-size:11.5px; color:var(--text-faint); line-height:1.5; max-width:62ch }
/* The control column: pinned right, never shrinks below something usable, and
   allowed to give ground before the text does on a narrow window. */
.settings-row .btn{ flex:0 0 auto }
.settings-row .settings-slider-wrap{ flex:0 1 340px; min-width:200px; margin-top:0 }
/* Below this the two columns stop fitting side by side and stack instead. */
@media (max-width:900px){
  .settings-row{ flex-direction:column; align-items:stretch; gap:10px }
  .settings-row .settings-slider-wrap{ flex:1 1 auto; max-width:none }
}


/* ================================================================
   Phase 5a — touch input integrity & hit targets (mobile Option 1)
   Everything below is inert for a mouse: touch-action only constrains
   touch gestures, and every layout-affecting rule sits behind
   @media (pointer:coarse), which a desktop never evaluates. The
   desktop screenshot gate for this phase is BYTE-ZERO diff.
   ---------------------------------------------------------------- */
/* The EQ dots use pointer events and cancel pointerdown — but canceling
   pointerdown does NOT stop the browser claiming the gesture for
   scrolling; only touch-action does. Without this line, a vertical
   (gain-axis) drag scrolls the page underneath the drag — reproduced
   in the 5a touch harness as scrollY 140 -> 93 mid-drag. */
.studio-dot{touch-action:none}
/* Same disease on the scrubber, which also had {passive:true} touch
   listeners (fixed in ui-mixer.js alongside this). */
.scrub-track{touch-action:none}
/* Invisible enlarged touch ring behind each EQ dot, created by
   ui-mixer.js only when the pointer is coarse. pointer-events:all makes
   the unpainted interior touchable; nothing is ever drawn. */
.studio-dot-hit{fill:none;stroke:none;pointer-events:all;touch-action:none}
/* Hit-area enlargement for coarse pointers only. Each ::before/::after
   is a transparent overlay: the VISUAL stays exactly as designed, only
   the touchable area grows (Apple minimum 44pt). Events on a pseudo
   target its owner element, so every existing handler keeps working. */
@media (pointer:coarse){
  .scrub-track::before{content:'';position:absolute;left:0;right:0;top:-14px;bottom:-14px}
  .fader::after{content:'';position:absolute;top:0;bottom:0;left:-7px;right:-7px}
  .strip-del::after{content:'';position:absolute;inset:-12px}
  .pill-toggle::after{content:'';position:absolute;inset:-12px}
  /* tok-chips sit in a wrapping grid with 7px gaps — an overlay would
     overlap the neighbouring chip, so these genuinely grow on touch
     devices instead (30px -> 44px tall). Desktop padding unchanged. */
  .tok-chip{padding:13px 16px}
}
