:root {
  --mo-accent: #2c7a7b;                  /* teal — music/album semantic color */
  /* Charter palette + role accent — OPERATOR (dev) defaults: warm.
     spec-musicorgan-operator-player-organ-banner. .mo-player overrides below.
     Role (warm/cool) is ORTHOGONAL to theme (light/dark): role drives the accent
     + mascot skin only; banner/surface darkness is theme-driven (tokens below). */
  --charter-body: #ff905f;
  --charter-shade:#e76a39;               /* deeper skin tone for the blend */
  --charter-arm:  #ffaf8f;
  --charter-accent:#4aa3ff;              /* opposite-hue accent — blue weaves through the orange */
  --charter-eye:  #ffffff;
  --charter-pupil:#2b3b47;
  --mo-role-accent: #ff8c42;             /* warm — Operator */

  /* ===== Style system — semantic tokens (spec-musicorgan-style-system) =====
     LIGHT is the default theme (operator preference: light, CTAs not dark).
     [data-bs-theme="dark"] below restores the prior dark mood. Every bespoke
     color in this sheet should reference a token; no new raw hex outside these
     two blocks. Stock Bootstrap components follow data-bs-theme on their own. */
  --mo-surface:    #f6f8fb;              /* page background */
  --mo-surface-2:  #ffffff;              /* cards / elevated panels */
  --mo-surface-3:  #eef2f7;             /* inset / row hover */
  --mo-border:     #d8e0ea;
  --mo-text:       #1a202c;
  --mo-text-muted: #5b6b7f;
  --mo-banner-bg:    linear-gradient(#ffffff, #eef2f7);
  --mo-banner-text:  #1a202c;
  --mo-banner-muted: #5b6b7f;
  --mo-cta-bg:       #ffffff;            /* LIGHT cta button — not a dark fill */
  --mo-cta-text:     #1a202c;
  --mo-cta-border:   #cbd5e0;
  --mo-cta-hover-bg: #eef2f7;
  --mo-stage-bg:     #eef2f7;            /* now-playing stage */
  --mo-stage-text:   #1a202c;
  --mo-stage-muted:  #5b6b7f;
}
/* PLAYER (Fly public read-only): cool/blue palette + accent. Role only — no
   banner/theme color here (that moved to the theme tokens above). */
.mo-player {
  --charter-body: #5aa9ff;
  --charter-shade:#2f7fd6;               /* deeper skin tone for the blend */
  --charter-arm:  #9fccff;
  --charter-accent:#ff8a4c;              /* opposite-hue accent — orange weaves through the blue */
  --charter-eye:  #ffffff;
  --charter-pupil:#16263a;
  --mo-role-accent: #38bdf8;             /* cool — Player */
}
/* DARK theme — restores the prior dark mood as an opt-in via the banner toggle. */
[data-bs-theme="dark"] {
  --mo-surface:    #0f1626;
  --mo-surface-2:  #1a202c;
  --mo-surface-3:  #222a38;
  --mo-border:     rgba(255,255,255,.12);
  --mo-text:       #e2e8f0;
  --mo-text-muted: #94a3b8;
  --mo-banner-bg:    linear-gradient(#20283a, #1a202c);
  --mo-banner-text:  #f1f5f9;
  --mo-banner-muted: rgba(255,255,255,.6);
  --mo-cta-bg:       rgba(255,255,255,.06);
  --mo-cta-text:     rgba(255,255,255,.92);
  --mo-cta-border:   rgba(255,255,255,.18);
  --mo-cta-hover-bg: rgba(255,255,255,.14);
  --mo-stage-bg:     #1a202c;
  --mo-stage-text:   #e2e8f0;
  --mo-stage-muted:  #cbd5e0;
}
body { background: var(--mo-surface); color: var(--mo-text); }
.navbar-brand i { color: var(--mo-accent); }
/* banner text reads from theme tokens (we drop Bootstrap's navbar-dark) */
.mo-banner .navbar-brand, .mo-banner .navbar-brand .fw-bold,
.mo-banner .nav-link, .mo-banner .navbar-toggler { color: var(--mo-banner-text); }
.mo-banner .navbar-toggler { border-color: var(--mo-border); }
.mo-theme-toggle { color: var(--mo-banner-text); background: transparent; border: 1px solid var(--mo-border); }
.mo-theme-toggle:hover, .mo-theme-toggle:focus-visible { color: var(--mo-role-accent); border-color: var(--mo-role-accent); }

/* ---- banner + role badge ---- */
.mo-banner { background: var(--mo-banner-bg); }
.mo-role-badge {
  display:inline-flex; align-items:center; gap:.3rem;
  font-size:.68rem; font-weight:700; text-transform:uppercase; letter-spacing:.06em;
  padding:.12rem .55rem; border-radius:1rem; margin-left:.55rem; color:#fff;
  background: var(--mo-role-accent); border:1px solid rgba(255,255,255,.28);
  white-space:nowrap;
}

/* ---- operator catalog switcher (spec-musicorgan-operator-catalog-switcher) ---- */
/* A compact navbar selector sitting beside the role badge: shows the ACTIVE
   catalog's label; the dropdown lists the operator's choices grouped by archive,
   with the active one checked. Operator-only (the includer guards on !read_only). */
.mo-catalog-switch .dropdown-toggle {
  font-size:.72rem; font-weight:600; max-width:14rem;
  border-color: var(--mo-border); color: var(--mo-banner-text);
}
.mo-catalog-switch .mo-catalog-active-label {
  max-width:9rem; overflow:hidden; text-overflow:ellipsis; white-space:nowrap;
}
.mo-catalog-menu { max-height:70vh; overflow-y:auto; min-width:16rem; }
.mo-catalog-menu .dropdown-item.active,
.mo-catalog-menu .dropdown-item.active:focus {
  background: var(--mo-role-accent); color:#fff;
}
.mo-catalog-menu form { display:block; }

/* ---- Charter mascot: palette via CSS vars + idle animation ---- */
.charter { width: 2.6rem; height: 2.6rem; display:block; overflow:visible; }
/* hero variant on the Charter home: large, centered, soft drop shadow */
.charter--hero { width: clamp(8rem, 22vw, 13rem); height: clamp(8rem, 22vw, 13rem);
  margin-inline:auto; filter: drop-shadow(0 10px 20px rgba(0,0,0,.18)); }
.mo-role-badge--hero { font-size:.8rem; padding:.3rem .85rem; margin-left:0; }
/* brand lockup tagline: "Music Organ" sitting under the Charter wordmark.
   Muted by default (light login card); brightened on the dark banner. */
.mo-tagline {
  font-size:.62rem; font-weight:600; line-height:1;
  text-transform:uppercase; letter-spacing:.08em;
  color: var(--mo-banner-muted);
}
/* body wears the living-skin gradient (animated stop-colors below) + mottle filter */
.charter-body  { fill: url(#charter-skin-grad); }
.charter-arm   { fill: var(--charter-arm); }
/* chromatophore skin: 4 stops drift between the role's skin tones AND the
   opposite-hue accent, desynced — blues and oranges weave across the body.
   Stop 3 anchors the accent so the second hue is always visible. */
.charter-stop--1 { stop-color: var(--charter-shade);  animation: charter-skin-1 9s  ease-in-out infinite; }
.charter-stop--2 { stop-color: var(--charter-body);   animation: charter-skin-2 11s ease-in-out infinite; }
.charter-stop--3 { stop-color: var(--charter-accent); animation: charter-skin-3 8s  ease-in-out infinite; }
.charter-stop--4 { stop-color: var(--charter-arm);    animation: charter-skin-4 6.5s ease-in-out infinite; }
@keyframes charter-skin-1 { 0%,100% { stop-color: var(--charter-shade); }  50% { stop-color: var(--charter-accent); } }
@keyframes charter-skin-2 { 0%,100% { stop-color: var(--charter-body); }   50% { stop-color: var(--charter-arm); } }
@keyframes charter-skin-3 { 0%,100% { stop-color: var(--charter-accent); } 50% { stop-color: var(--charter-body); } }
@keyframes charter-skin-4 { 0%,100% { stop-color: var(--charter-arm); }    50% { stop-color: var(--charter-shade); } }
.charter-eye   { fill: var(--charter-eye); }
.charter-pupil-fill { fill: var(--charter-pupil); }
/* gentle whole-mascot sway — reads as arms drifting left/right + up/down */
.charter-arms { transform-box: fill-box; transform-origin: 50% 35%; animation: charter-sway 6s ease-in-out infinite; }
@keyframes charter-sway {
  0%   { transform: rotate(-2.6deg) translateY(0); }
  50%  { transform: rotate(2.6deg)  translateY(-12px); }
  100% { transform: rotate(-2.6deg) translateY(0); }
}
/* pupils dart on independent timings — the two eyes do NOT move together */
.charter-pupil { transform-box: fill-box; }
.charter-pupil--l { animation: charter-dart-l 5.3s ease-in-out infinite; }
.charter-pupil--r { animation: charter-dart-r 7.1s ease-in-out infinite; }
@keyframes charter-dart-l {
  0%,18%   { transform: translate(0,0); }
  26%,40%  { transform: translate(8px,-4px); }
  52%,66%  { transform: translate(-6px,5px); }
  78%,100% { transform: translate(5px,1px); }
}
@keyframes charter-dart-r {
  0%,12%   { transform: translate(-5px,3px); }
  30%,44%  { transform: translate(7px,4px); }
  58%,72%  { transform: translate(3px,-5px); }
  84%,100% { transform: translate(-4px,0); }
}
/* conductor's baton: the raised arm holds a baton; the conducting WAVE is the idle
   animation. The baton tapers to a point (its own taper = the tapered tip) and
   waves in free space beyond the arm, so no masking/blunting of the octopus arm.
   Pivots at the grip, desynced from the body sway / eye darts / skin.
   spec-musicorgan-charter-arm-wiggle */
/* plain uniform rod: a dark edge under a white core, rounded tip. NOT tapered —
   the octopus arm keeps its own natural smooth tip (that was what blunted earlier). */
.charter-baton-shaft-edge { fill: none; stroke: rgba(35,24,12,.55); stroke-width: 4.4; stroke-linecap: round; }
.charter-baton-shaft      { fill: none; stroke: #fbfaf5;          stroke-width: 3.0; stroke-linecap: round; }
/* classic dark cork handle (not themed — role is signalled elsewhere) */
.charter-baton-grip  { fill: #241d15; stroke: rgba(15,10,5,.55); stroke-width: 1; }
.charter-baton {
  transform-box: fill-box; transform-origin: 16% 90%;
  animation: charter-baton-wave 2.8s cubic-bezier(0.4,0,0.2,1) infinite;
}
@keyframes charter-baton-wave {
  0%   { transform: rotate(-13deg); }
  50%  { transform: rotate(13deg); }
  100% { transform: rotate(-13deg); }
}
@media (prefers-reduced-motion: reduce) {
  .charter-arms, .charter-pupil--l, .charter-pupil--r, .charter-baton,
  .charter-stop--1, .charter-stop--2, .charter-stop--3, .charter-stop--4 { animation: none !important; }
}
/* dev inspect aid — append ?inspect=armtip to the URL. Outlines the baton in cyan
   and slows the conducting wave (SAME ±13deg, longer 9s period) so the motion is
   easy to follow on the live page. Gated on <html class="inspect-armtip">; never
   affects the default render. spec-clodi-leaning-motion-tunicate. */
.inspect-armtip .charter-baton-shaft { stroke: #00e5ff; }
.inspect-armtip .charter-baton-grip  { stroke: #00e5ff; stroke-width: 2.5; }
.inspect-armtip .charter-baton { animation-duration: 9s; }
.inspect-armtip body::after {
  content: "INSPECT · cyan = .charter-baton (conductor's baton) · real ±13° conducting wave, slowed to 9s · drop ?inspect=armtip to restore";
  position: fixed; left: 0; right: 0; bottom: 0; z-index: 2000;
  background: #00121a; color: #00e5ff; text-align: center;
  font: 600 .78rem/1.7 system-ui, sans-serif; letter-spacing: .02em; padding: .35rem;
}

/* ---- organ manual: large black & white keys, hover-depress ---- */
/* navbar-expand-lg forces flex-wrap:nowrap at >=992px, which squeezed the keys
   onto the content row. Make the sticky banner a plain BLOCK stack instead:
   the inner .container-fluid keeps Bootstrap's own display:flex (brand/search
   row), and .mo-keys + #mo-progress fall below it full-width. A block sticky
   element avoids the flex-direction:column + position:sticky resize glitch
   (banner drifting down, whitespace above). */
.mo-banner.navbar { display: block; }
/* no hamburger (spec-musicorgan-banner-no-hamburger): the banner row stays inline
   at every width, so let it wrap on narrow screens instead of overflowing. */
.mo-banner > .container-fluid { flex-wrap: wrap; row-gap: .3rem; }
.mo-keys { position: relative; width: 100%; height: 24px; user-select: none; border-top: 2px solid var(--mo-role-accent); }
.mo-keys-white { display: flex; height: 100%; }
.mo-key-w {
  flex: 1 1 0; background: linear-gradient(#fff, #ededed);
  border-right: 1px solid #b9b9b9; border-bottom: 2px solid rgba(0,0,0,.28);
  transform-origin: top; transition: transform .08s ease, background .08s ease;
}
.mo-key-w:last-child { border-right: 0; }
.mo-key-w:hover { background: linear-gradient(var(--mo-role-accent), #fff); transform: scaleY(.9); border-bottom-width: 1px; }
.mo-keys-black { position: absolute; inset: 0 0 auto 0; height: 62%; pointer-events: none; }
.mo-key-b {
  position: absolute; top: 0; width: 4.3%; height: 100%;
  background: linear-gradient(#3a3a3a, #050505); border-radius: 0 0 3px 3px;
  box-shadow: 0 2px 2px rgba(0,0,0,.5); pointer-events: auto;
  transform-origin: top; transition: transform .08s ease, background .08s ease;
}
.mo-key-b:hover { background: linear-gradient(var(--mo-role-accent), #1a1a1a); transform: scaleY(.88); }
.mo-key-b:nth-child(1) { left: 4.99%; }
.mo-key-b:nth-child(2) { left: 12.14%; }
.mo-key-b:nth-child(3) { left: 26.42%; }
.mo-key-b:nth-child(4) { left: 33.56%; }
.mo-key-b:nth-child(5) { left: 40.71%; }
.mo-key-b:nth-child(6) { left: 54.99%; }
.mo-key-b:nth-child(7) { left: 62.14%; }
.mo-key-b:nth-child(8) { left: 76.42%; }
.mo-key-b:nth-child(9) { left: 83.56%; }
.mo-key-b:nth-child(10){ left: 90.71%; }
.table-hover tbody tr:hover { background: #f0fbfb; }
.count-badge { font-variant-numeric: tabular-nums; }

/* htmx in-flight bar under the navbar */
.mo-progress { height: 2px; background: transparent; }
.mo-progress.htmx-request { background: linear-gradient(90deg, var(--mo-accent), #4fd1c5); animation: mo-pulse 1s ease-in-out infinite; }
@keyframes mo-pulse { 0%,100% { opacity:.4 } 50% { opacity:1 } }

/* ---- persistent player bar (now a static row inside the sticky top header) ---- */
.mo-topbar { z-index: 1030; background: var(--mo-surface); }  /* banner + Now Playing pin together at top; OPAQUE so the scrolling list never bleeds through (spec-musicorgan-now-playing-opaque-header) */
/* top-of-list == bottom of the player: any scrollIntoView/anchor jump lands the
   target BELOW the sticky header, not hidden behind the Now-Playing widget.
   --mo-topbar-h is measured live in mo-player.js (trackTopbarHeight). */
html { scroll-padding-top: var(--mo-topbar-h, 0px); }
/* now-playing stage — scoped to #mo-player so these layout/color rules never
   bleed onto body.mo-player (the role class shares the name). Themed via stage
   tokens; the dark-stage utility classes inside are re-skinned just below. */
#mo-player {
  background: var(--mo-stage-bg); color: var(--mo-stage-text);
  min-height: 20vh; display: flex; flex-direction: column; justify-content: center;
  gap: .35rem; padding-block: .6rem;
}
/* re-skin the player bar's Bootstrap dark-stage utilities through tokens so the
   stage follows the theme without editing the template's utility classes. */
#mo-player .btn-outline-light { color: var(--mo-stage-text); border-color: var(--mo-border); }
#mo-player .btn-outline-light:hover, #mo-player .btn-outline-light:focus-visible {
  background: var(--mo-role-accent); border-color: var(--mo-role-accent); color: #fff;
}
#mo-player .text-light { color: var(--mo-stage-text) !important; }
#mo-player .text-secondary { color: var(--mo-stage-muted) !important; }
.mo-player-stage { width: 100%; }
/* big control satellites around the now-playing portlet */
.mo-pbtn { font-size: 1.5rem; line-height: 1; padding: .35rem .6rem; border-radius: .6rem; }
.mo-pbtn-lg { font-size: 2.4rem; padding: .2rem .75rem; border-radius: 50%; }
.mo-pbtn .badge { font-size: .6rem; }
/* now-playing portlet (centre): big art + big title */
.mo-portlet { min-width: 0; max-width: 42vw; cursor: pointer; }
.mo-portlet-art { height: clamp(72px, 13vh, 132px); width: clamp(72px, 13vh, 132px); object-fit: cover; background:#e6fffa; }
.mo-portlet-title { font-size: 1.4rem; font-weight: 600; line-height: 1.2; }
.mo-portlet-sub { font-size: 1.05rem; }
.mo-status-line { font-size: .82rem; opacity: .85; }
/* THE single control bar (spec-musicorgan-single-unified-player-transport):
   headless engine renders nothing; the transport + scrubber + time + volume ARE
   its UI. The native <audio controls> bar (the old "second player") is gone. */
.mo-audio-headless { display: none; }
/* full-width scrubber row under the stage */
#mo-player .mo-seek {
  flex: 1 1 auto; width: auto; min-width: 120px; max-width: none;
  height: 6px; accent-color: var(--mo-role-accent); cursor: pointer; margin: 0;
}
.mo-time { font-variant-numeric: tabular-nums; font-size: .9rem; white-space: nowrap; }
/* LEFT zone: transport stacked over volume; the slider fills the transport width */
.mo-player-center { min-width: 0; }      /* let the card title truncate inside flex */
#mo-player .mo-vol { width: 124px; accent-color: var(--mo-role-accent); cursor: pointer; margin: 0; }
#mo-player .mo-volume .mo-pbtn { font-size: 1.1rem; padding: .2rem .4rem; }
/* RIGHT zone — queue / favourite / locate / info, sitting to the right of the
   now-playing card (spec-musicorgan-single-unified-player-transport). Spatially
   separated, so no divider needed. */
.mo-player-utils .mo-pbtn { font-size: 1.2rem; padding: .25rem .45rem; }
/* absorbed album card — filled when the on-screen album IS the one playing; the
   AlbumView hero is suppressed so it shows once, in the player. */
.mo-now-album { max-width: 34vw; padding-left: 1rem; border-left: 1px solid var(--mo-border); }
.mo-now-album-title { font-size: 1.15rem; }
.mo-now-album-sub { font-size: 1.05rem; }
/* !important: the hero keeps Bootstrap's .d-flex (display:flex !important), which
   would otherwise win (leaning-bootstrap-d-flex-overrides-hidden-attr). */
body.mo-merged #mo-av-header { display: none !important; }

/* MERGED layout (Kris 2026-06-14, spec-musicorgan-player-absorbs-album-merge amend):
   the absorbed album block STACKS ABOVE the now-playing track (not side-by-side).
   #mo-now-album sits on top (order:-1) carrying album title·year·id·Play album; the
   track portlet renders below. order: keeps the DOM (clone target last) but paints
   album first. Drop the side divider; left-align the stack. */
body.mo-merged .mo-player-center { flex-direction: column; align-items: flex-start; gap: .35rem; }
body.mo-merged #mo-now-album { order: -1; max-width: 100%; padding-left: 0; border-left: 0; }
body.mo-merged .mo-now-album-line { white-space: normal; }

/* OWL · Metadata operator card (spec-musicorgan-owl-metadata-operator-card) */
.mo-owl-card .card-header { background: var(--mo-surface-2, rgba(0,0,0,.03)); }
.mo-owl-flabel { width: 5.5rem; flex: 0 0 5.5rem; }
.mo-owl-input { max-width: 24rem; }
.mo-owl-genre-add { width: 9rem; }
.mo-owl-genre-x { cursor: pointer; color: inherit; line-height: 1; }
.mo-owl-field + .mo-owl-field { border-top: 1px dashed var(--mo-border); }
/* click-to-edit fields: value reads as plain text, hints editable on hover */
.mo-owl-display { cursor: text; border-bottom: 1px dashed transparent; padding: 0 1px; }
.mo-owl-display:hover { border-bottom-color: var(--mo-border); }
.mo-owl-row .mo-owl-input { max-width: 22rem; min-width: 8rem; }
.mo-owl-overdot { font-size: .72rem; }
.mo-owl-altlabel { white-space: nowrap; }
/* Narrow phones (spec-musicorgan-narrow-header-shrink): shrink the banner +
   player chrome so the header stops wrapping onto extra rows. Drop the
   Operator/Player badge, trim the brand mark, and make the player control
   satellites + portlet smaller — reduce button size rather than line-wrap. */
@media (max-width: 480px) {
  .mo-role-badge { display: none; }
  .charter { width: 2.1rem; height: 2.1rem; }
  #mo-player .mo-player-stage { gap: .5rem; }
  #mo-player .mo-player-stage > .d-flex { gap: .3rem; }
  .mo-pbtn { font-size: 1.15rem; padding: .2rem .4rem; }
  .mo-pbtn-lg { font-size: 1.55rem; padding: .15rem .5rem; }
  .mo-portlet-art { height: 48px; width: 48px; }
  .mo-portlet-title { font-size: 1.05rem; }
  .mo-portlet-sub { font-size: .82rem; }
  .mo-now-album { max-width: 60vw; padding-left: .5rem; }
  .mo-now-album-title { font-size: .95rem; }
  .mo-now-album-sub { font-size: .82rem; }
  .mo-player-utils { padding-left: .4rem; }
  /* keep the single control bar on ONE row — shrink rather than wrap: drop the
     volume slider (mute button stays), tighten the scrubber + time. */
  #mo-player .mo-volume .mo-vol { display: none; }
  #mo-player .mo-seek { min-width: 64px; }
  .mo-time { font-size: .78rem; }
}
/* current-track ID reveal under the strip (own classes; not the canonical mo-id-chip) */
.mo-now-id { display: flex; }
.mo-nowid-text { color: var(--mo-stage-muted); }
.mo-nowid-copy.mo-nowid-copied { color: #34d399; border-color: #34d399; }
/* "show in list" locate flash on the target row */
.mo-row-flash { animation: mo-row-flash 1.3s ease-out; }
@keyframes mo-row-flash { 0%,25% { background: #ffe9a8 !important; } 100% { background: transparent; } }
@media (prefers-reduced-motion: reduce) { .mo-row-flash { animation: none !important; } }
.mo-now { min-width: 0; }
.mo-now-art { height: 38px; width: 38px; object-fit: cover; }
/* album-art thumbnails in cards/grids */
.mo-art { object-fit: cover; background: #e6fffa; }
.mo-art-card { width: 56px; height: 56px; flex: 0 0 56px; }
.mo-art-detail { width: 72px; height: 72px; }
#mo-player .btn-outline-light.active { background: var(--mo-accent); border-color: var(--mo-accent); color:#fff; }

/* up-next drawer */
.mo-queue-drawer { background:var(--mo-surface-3); color:var(--mo-stage-text); max-height: 45vh; overflow-y:auto; }
.mo-queue-list li { display:flex; align-items:center; gap:.5rem; padding:.25rem .75rem; cursor:pointer; border-bottom:1px solid var(--mo-border); }
.mo-queue-list li:hover { background:var(--mo-surface-2); }
.mo-queue-list li.mo-current { background:#2c7a7b33; }
.mo-queue-list li.mo-current .mo-q-title { color:var(--mo-accent); font-weight:600; }
.mo-queue-list .mo-q-title { flex:1; min-width:0; white-space:nowrap; overflow:hidden; text-overflow:ellipsis; }
.mo-queue-list .mo-q-sub { color:var(--mo-stage-muted); font-size:.8em; }
.mo-queue-list .mo-q-rm { color:var(--mo-stage-muted); }
.mo-queue-list .mo-q-rm:hover { color:#f56565; }

/* play/now-playing affordances in lists — SLICE 3 now-playing treatment
   (spec-musicorgan-playback-focus-and-nav-prominence): clear accent bar + tint +
   a small animated equaliser glyph injected by highlightPlaying(). Visual only. */
tr.mo-row-playing { background:#e6fffa !important; }
tr.mo-row-playing td:first-child { box-shadow: inset 4px 0 0 var(--mo-accent); }
tr.mo-row-playing td.fw-medium, tr.mo-row-playing .fw-medium { color: var(--mo-accent); }
.mo-eq {
  color: var(--mo-accent); margin-right:.3rem; font-size:1rem; vertical-align:-0.1em;
  animation: mo-eq-pulse 1.1s ease-in-out infinite;
}
@keyframes mo-eq-pulse { 0%,100% { opacity:.45; transform:scaleY(.78); } 50% { opacity:1; transform:scaleY(1); } }
@media (prefers-reduced-motion: reduce) { .mo-eq { animation: none; opacity:1; } }

/* SLICE 2 — sibling-album badges in the artist header (compact wrap-row of small
   owl chips; NOT full cards). TAP the chip = focus (hx-get); the play glyph on it
   = play the album (.mo-play-list) without changing focus. Keep chips dense. */
.mo-album-badge-row { display:flex; flex-wrap:wrap; gap:.4rem .5rem; align-items:center; }
.mo-album-badge-wrap { display:inline-flex; align-items:center; }
.mo-album-badge {
  display:inline-flex; align-items:center; gap:.15rem;
  padding:.2rem .5rem; border:1px solid var(--mo-border); border-radius:1rem;
  background:var(--mo-surface-2); color:var(--mo-text); font-size:.82rem; line-height:1.2;
  max-width:16rem; transition:border-color .12s ease, background .12s ease;
}
.mo-album-badge:hover, .mo-album-badge:focus-visible { border-color:var(--mo-accent); background:var(--mo-surface-3); color:var(--mo-text); }
.mo-album-badge-name { overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
.mo-album-badge-yr { color:var(--mo-text-muted); font-variant-numeric:tabular-nums; }
.mo-album-badge-play {
  display:inline-flex; align-items:center; justify-content:center;
  width:1.7rem; height:1.7rem; margin-left:-.55rem;
  border:1px solid var(--mo-border); border-left:0; border-radius:0 1rem 1rem 0;
  background:var(--mo-surface-2); color:var(--mo-accent); cursor:pointer; font-size:.9rem;
}
.mo-album-badge-play:hover, .mo-album-badge-play:focus-visible { background:var(--mo-accent); color:#fff; border-color:var(--mo-accent); }

/* SLICE 4 — inline "You might like" pinned under the focused tracklist. */
.mo-you-might-like { background:var(--mo-surface-2); }

/* inline track-detail expansion row */
tr.mo-detail-row > td { background:#f8fafc; }
.mo-facts { display:grid; grid-template-columns: repeat(auto-fill, minmax(11rem,1fr)); gap:.35rem 1.25rem; }
.mo-facts dt { font-size:.7rem; text-transform:uppercase; letter-spacing:.04em; color:#718096; margin:0; }
.mo-facts dd { margin:0 0 .35rem 0; font-size:.9rem; }
.mo-mb { font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size:.72rem; }

/* star rating widget */
.mo-stars { display:inline-flex; cursor:pointer; }
.mo-stars i { color:#cbd5e0; }
.mo-stars i.on { color:#ecc94b; }

/* row-ID affordance — tiny muted chip + inline copy popover (no layout shift) */
.mo-id-chip { display:inline-flex; align-items:center; gap:.2rem; vertical-align:baseline; }
.mo-id-text { font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size:.68rem; color:#a0aec0; line-height:1; white-space:nowrap; }
.mo-id-info { font-size:.72rem; color:#a0aec0 !important; line-height:1; text-decoration:none; }
.mo-id-info:hover { color:var(--mo-accent) !important; }
.mo-id-pop { display:inline-flex; align-items:center; }
.mo-id-copy { font-size:.68rem; line-height:1.1; }
.mo-id-copy.mo-id-copied { color:#22863a; border-color:#22863a; }

/* alpha jump bar */
.mo-alpha { display:flex; flex-wrap:wrap; gap:.15rem; }
.mo-alpha a { width:1.7rem; text-align:center; }
.mo-alpha a.active { background:var(--mo-accent); color:#fff; border-color:var(--mo-accent); }

/* transcode status badge on the now-playing portlet
   (spec-musicorgan-transcode-web-rendition) — appended block, do not reorder */
.mo-transcode-badge {
  margin-top:.15rem; font-size:.72rem; line-height:1.2; font-weight:600;
  display:inline-flex; align-items:center; gap:.3rem;
  padding:.1rem .45rem; border-radius:.6rem;
}
.mo-transcode-wait { color:#0c2b34; background:#7fd1de; }
.mo-transcode-fail { color:#fff; background:#b23a48; }
.mo-transcode-wait::before { content:"\F130"; font-family:"bootstrap-icons"; animation:mo-tc-pulse 1.2s ease-in-out infinite; }
.mo-transcode-fail::before { content:"\F623"; font-family:"bootstrap-icons"; }
@keyframes mo-tc-pulse { 0%,100% { opacity:.4; } 50% { opacity:1; } }

/* ============ Browse CTAs — always-on Artists/Albums/Genres/Search ===========
   spec-musicorgan-responsive-browse-cta. One partial, two placements: inline in
   the banner at >=lg, a full-width touch bar at <lg (never in the hamburger). */
.mo-aviary { vertical-align: -0.18em; }

/* ============ Genres enrichment (spec-musicorgan-genres-enrichment) =========
   Curated-icon cards on the index + an artist word-cloud on the detail page.
   All token-themed (light default, dark via [data-bs-theme="dark"]). */
.mo-genre-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(13rem, 1fr)); gap: .6rem; }
.mo-genre-card {
  display: flex; align-items: center; gap: .7rem; text-decoration: none;
  padding: .7rem .8rem; border-radius: .7rem;
  background: var(--mo-surface-2); border: 1px solid var(--mo-border); color: var(--mo-text);
  transition: border-color .12s ease, background .12s ease;
}
.mo-genre-card:hover, .mo-genre-card:focus-visible { border-color: var(--mo-accent); background: var(--mo-surface-3); }
.mo-genre-card-icon, .mo-genre-head-icon {
  flex: 0 0 auto; display: inline-flex; align-items: center; justify-content: center;
  border-radius: .55rem; color: var(--mo-accent);
  background: color-mix(in srgb, var(--mo-accent) 14%, transparent);
}
.mo-genre-card-icon { width: 2.2rem; height: 2.2rem; font-size: 1.15rem; }
.mo-genre-head-icon { width: 3rem; height: 3rem; font-size: 1.6rem; border-radius: .7rem; }
.mo-genre-card-body { display: flex; flex-direction: column; min-width: 0; }
.mo-genre-card-name { font-weight: 600; line-height: 1.2; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.mo-genre-card-counts { font-size: .78rem; color: var(--mo-text-muted); }

/* artist word-cloud — chips sized by track count (font-size set inline) */
.mo-wordcloud { display: flex; flex-wrap: wrap; align-items: baseline; gap: .3rem .8rem; line-height: 1.5; }
.mo-word {
  text-decoration: none; color: var(--mo-text);
  display: inline-flex; align-items: baseline; gap: .25rem;
  border-bottom: 1px solid transparent;
}
.mo-word:hover, .mo-word:focus-visible { color: var(--mo-accent); border-bottom-color: var(--mo-accent); }
.mo-word-xy { font-size: .62em; color: var(--mo-text-muted); font-variant-numeric: tabular-nums; }
/* LIGHT cta buttons (spec-musicorgan-style-system): light fill + dark text +
   defined border; the role accent is a hover/focus tint, never a dark fill. */
.mo-cta {
  display: inline-flex; align-items: center; gap: .4rem;
  text-decoration: none; color: var(--mo-cta-text);
  border: 1px solid var(--mo-cta-border); border-radius: .5rem;
  background: var(--mo-cta-bg); white-space: nowrap;
  transition: background .12s ease, border-color .12s ease, color .12s ease;
}
.mo-cta:hover, .mo-cta:focus-visible {
  color: var(--mo-cta-text); border-color: var(--mo-role-accent);
  background: var(--mo-cta-hover-bg);
}
.mo-cta .mo-cta-icon { flex: 0 0 auto; }
.mo-cta i.mo-cta-icon { font-size: 1.05rem; color: var(--mo-role-accent); }

/* NAVIGATOR bar — ONE large always-on band below the banner at EVERY width
   (spec-musicorgan-playback-focus-and-nav-prominence SLICE 1). The retired
   .mo-cta-inline navbar fork is gone (the 992px bug). "More icon, less box":
   the glyph dominates and the chrome is trimmed, while the tap target stays
   >=44px. It's now a direct child of <header> (not inside .navbar), so the
   old 100vw breakout is unneeded — a plain full-width flex band suffices. */
.mo-cta-bar {
  display: flex; gap: .25rem;
  padding: .3rem .4rem;
  width: 100%; box-sizing: border-box;
  background: var(--mo-stage-bg); border-top: 1px solid var(--mo-border);  /* OPAQUE — blends into the player stage tone; list must not scroll behind the nav verbs (spec-musicorgan-now-playing-opaque-header) */
}
.mo-cta-bar .mo-cta {
  flex: 1 1 0; min-width: 0; min-height: 56px; overflow: hidden;
  flex-direction: column; gap: .1rem; padding: .25rem .2rem;
  font-size: .8rem; font-weight: 600; line-height: 1.05;
  border: 0; background: transparent; border-radius: .4rem;
  color: var(--mo-cta-text);
  transition: background .12s ease, color .12s ease;
}
/* bigger glyph, tighter chrome — the 22→28 bump wasn't enough; push to 40px. */
.mo-cta-bar .mo-cta .mo-aviary { width: 40px; height: 40px; transition: transform .12s ease; }
.mo-cta-bar .mo-cta i.mo-cta-icon { font-size: 1.9rem; }
.mo-cta-bar .mo-cta-label { line-height: 1; max-width: 100%; }
/* hover = raised chip. The band now sits on the stage tone, so the old
   --mo-cta-hover-bg (== stage tone in light) would vanish; lift to the elevated
   surface instead, which contrasts against the band in both themes. */
.mo-cta-bar .mo-cta:hover, .mo-cta-bar .mo-cta:focus-visible {
  background: var(--mo-surface-2); color: var(--mo-cta-text); border-color: transparent;
}
[data-bs-theme="dark"] .mo-cta-bar .mo-cta:hover,
[data-bs-theme="dark"] .mo-cta-bar .mo-cta:focus-visible {
  background: var(--mo-surface-3);
}
.mo-cta-bar .mo-cta:hover .mo-aviary,
.mo-cta-bar .mo-cta:focus-visible .mo-aviary { transform: translateY(-2px) scale(1.06); }
.mo-cta-bar .mo-cta:hover .mo-cta-label,
.mo-cta-bar .mo-cta:focus-visible .mo-cta-label {
  text-decoration: underline; text-underline-offset: 3px;
}
/* iPhone-SE / very narrow: trim a hair but keep glyph dominant + label readable. */
@media (max-width: 360px) {
  .mo-cta-bar { gap: .2rem; padding: .25rem .25rem; }
  .mo-cta-bar .mo-cta { min-height: 50px; padding: .2rem .1rem; font-size: .74rem; }
  .mo-cta-bar .mo-cta .mo-aviary { width: 32px; height: 32px; }
  .mo-cta-bar .mo-cta i.mo-cta-icon { font-size: 1.55rem; }
}

/* Search landing hero + on-page field
   (spec-musicorgan-player-usability-icons-search-crosslinks B). */
.mo-search-form { max-width: 38rem; }
.mo-search-hero .mo-aviary { filter: drop-shadow(0 4px 10px rgba(0,0,0,.12)); }

/* ============ Cloud-player cold-start "Charter is waking" cue ==================
   spec-musicorgan-player-cold-start-waking. While the Fly machine is cold (a
   transport click queued, awaiting first backend response) the transport wears a
   light, NON-BLOCKING waking state: a slim themed strip at the top of the player
   ("Charter is waking…") + a dimmed, busy-cursor look on the gated buttons. The
   action is queued, not dropped — this is purely the visual cue. Cleared the
   instant the backend responds (stream headers / healthz 200). Charter/lair motif
   via the moon-stars owl glyph + the Charter accent. Reduced-motion -> static. */
.mo-waking {
  display: flex; align-items: center; gap: .45rem;
  padding: .2rem .75rem; font-size: .78rem; font-weight: 600;
  color: var(--charter-eye, #fff);
  background: linear-gradient(90deg, var(--charter-shade, #2f7fd6), var(--charter-accent, #4aa3ff));
  letter-spacing: .01em;
}
.mo-waking[hidden] { display: none; }
.mo-waking-owl { display: inline-flex; }
.mo-waking-owl .bi { animation: mo-waking-rouse 1.4s ease-in-out infinite; transform-origin: center; }
@keyframes mo-waking-rouse {
  0%, 100% { opacity: .55; transform: translateY(0) rotate(-6deg); }
  50%      { opacity: 1;   transform: translateY(-1px) rotate(6deg); }
}
/* Gated transport buttons read as "rousing": dimmed + busy cursor, gentle pulse.
   Still clickable (a second click just re-queues the latest intent). */
.mo-waking-btn { opacity: .6; cursor: progress; animation: mo-waking-pulse 1.4s ease-in-out infinite; }
@keyframes mo-waking-pulse { 0%, 100% { opacity: .5; } 50% { opacity: .85; } }
@media (prefers-reduced-motion: reduce) {
  .mo-waking-owl .bi, .mo-waking-btn { animation: none !important; }
  .mo-waking-btn { opacity: .6; }   /* static dim — the label still reads "waking…" */
}

/* === spec-musicorgan-browse-grid-polish (appended; do not merge above) =======
   Defect 2: long card titles bled into the neighbor column. Bootstrap 5.3 has
   no `min-w-0` utility, so the flex child kept min-width:auto and refused to
   shrink below intrinsic content width, defeating the truncate. Define the
   utility once (benefits every flex child carrying `min-w-0`), and switch the
   card title from single-line ellipsis to a 2-line wrapping clamp (operator
   prefers wraps over ellipsis). `h-100` on the card keeps 1- and 2-line cards
   equal height in a row. */
.min-w-0 { min-width: 0 !important; }
.mo-card-title {
  min-width: 0;
  white-space: normal;
  overflow-wrap: anywhere;          /* break rare unbroken tokens before bleeding */
  /* !important so Bootstrap's d-* display utilities (display:…!important) can't
     defeat the -webkit-box clamp if the title <a> ever carries one. */
  display: -webkit-box !important;
  -webkit-line-clamp: 2;            /* wrap to 2 lines, then ellipsis */
  line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
}

/* === Shows / Sets browse — GONZO/STAINED_GLASS polish (appended; do not merge) ==
   spec-musicorgan-sets-phase3-browse-impl visual pass. /sets/ index gains real
   year groups, cover thumbnails, a source-type colour language + legend, and an
   illustrated Ravon empty state; /set/<id>/ gains a signal-chain provenance block
   + cross-link rail. All token-themed (light default + dark via data-bs-theme).
   No new data/routes — purely the SCOUT/GONZO layer over the shipped contract. */

/* --- source-type colour language (SBD/AUD/MTX/FM/UNK) ----------------------
   ONE legible palette shared by the index chips, the legend, and the show
   header, so a taper's source reads at a glance. Keyed by data-src so the
   view's Bootstrap source_class stays the rendered fallback (and the SBD/AUD
   text the verifier asserts is untouched). */
.mo-src {
  display: inline-flex; align-items: center; gap: .25rem;
  font-size: .72rem; font-weight: 700; letter-spacing: .03em;
  padding: .12rem .4rem; border-radius: .45rem; line-height: 1.1;
  border: 1px solid transparent; font-variant-numeric: tabular-nums;
}
.mo-src::before { font-family: "bootstrap-icons"; font-weight: normal; font-size: .9em; }
.mo-src[data-src="SBD"] { color:#0b3d1e; background:#bff0cf; border-color:#7bd69a; }  /* clean board feed — green */
.mo-src[data-src="SBD"]::before { content:"\F2E5"; }   /* plug — board */
.mo-src[data-src="AUD"] { color:#0a3a45; background:#bfe9f2; border-color:#7fcfe0; }  /* stealth mic — cyan */
.mo-src[data-src="AUD"]::before { content:"\F3D9"; }   /* mic */
.mo-src[data-src="MTX"] { color:#2a1b52; background:#d8cffb; border-color:#b3a3f0; }  /* matrix blend — violet */
.mo-src[data-src="MTX"]::before { content:"\F4E9"; }   /* sliders — blend */
.mo-src[data-src="FM"]  { color:#3a2c0a; background:#f7e6b0; border-color:#e6cf73; }  /* broadcast — amber */
.mo-src[data-src="FM"]::before { content:"\F2DA"; }    /* broadcast */
.mo-src[data-src="UNK"] { color:var(--mo-text-muted); background:var(--mo-surface-3); border-color:var(--mo-border); }
.mo-src[data-src="UNK"]::before { content:"\F505"; }   /* question */
[data-bs-theme="dark"] .mo-src[data-src="SBD"] { color:#bff0cf; background:rgba(123,214,154,.16); border-color:rgba(123,214,154,.4); }
[data-bs-theme="dark"] .mo-src[data-src="AUD"] { color:#bfe9f2; background:rgba(127,207,224,.16); border-color:rgba(127,207,224,.4); }
[data-bs-theme="dark"] .mo-src[data-src="MTX"] { color:#d8cffb; background:rgba(179,163,240,.18); border-color:rgba(179,163,240,.4); }
[data-bs-theme="dark"] .mo-src[data-src="FM"]  { color:#f7e6b0; background:rgba(230,207,115,.16); border-color:rgba(230,207,115,.4); }

.mo-src-legend { display:flex; flex-wrap:wrap; align-items:center; gap:.3rem .5rem; }
.mo-src-legend .mo-src { cursor:default; }

/* --- availability badge (ONLINE vs ARCHIVES) — make the duality unmistakable */
.mo-avail {
  display:inline-flex; align-items:center; gap:.3rem;
  font-size:.72rem; font-weight:700; letter-spacing:.02em;
  padding:.14rem .5rem; border-radius:1rem; line-height:1.1; white-space:nowrap;
}
.mo-avail-online { color:#0b3d1e; background:#c8f2d6; border:1px solid #7bd69a; }
.mo-avail-online .bi { color:#138a4a; }
.mo-avail-archive { color:#4a3503; background:#fbeec2; border:1px solid #e6cf73; }
.mo-avail-archive .bi { color:#9a6b00; }
[data-bs-theme="dark"] .mo-avail-online { color:#c8f2d6; background:rgba(123,214,154,.16); border-color:rgba(123,214,154,.4); }
[data-bs-theme="dark"] .mo-avail-online .bi { color:#7bd69a; }
[data-bs-theme="dark"] .mo-avail-archive { color:#fbeec2; background:rgba(230,207,115,.16); border-color:rgba(230,207,115,.4); }
[data-bs-theme="dark"] .mo-avail-archive .bi { color:#e6cf73; }

/* --- /sets/ year groups — collapsible section headers with counts + Ravon ---- */
.mo-set-group + .mo-set-group { margin-top: .9rem; }
.mo-set-group-head {
  display:flex; align-items:center; gap:.55rem; width:100%;
  padding:.45rem .7rem; cursor:pointer; text-align:left;
  background:var(--mo-surface-2); color:var(--mo-text);
  border:1px solid var(--mo-border); border-radius:.6rem;
  transition:border-color .12s ease, background .12s ease;
}
.mo-set-group-head:hover, .mo-set-group-head:focus-visible {
  border-color:var(--mo-accent); background:var(--mo-surface-3);
}
.mo-set-group-head .mo-aviary { flex:0 0 auto; }
.mo-set-group-yr { font-weight:700; font-size:1.05rem; font-variant-numeric:tabular-nums; }
.mo-set-group-meta { color:var(--mo-text-muted); font-size:.8rem; }
.mo-set-group-chev { margin-left:auto; color:var(--mo-text-muted); transition:transform .15s ease; }
.mo-set-group[open] .mo-set-group-chev { transform:rotate(90deg); }
.mo-set-group-head::-webkit-details-marker { display:none; }
.mo-set-group > summary { list-style:none; }
.mo-set-group > .card { margin-top:.4rem; }

/* --- /sets/ row thumbnail (reuse the AlbumArt cascade keyed by album_id) ----- */
.mo-set-thumb { width:38px; height:38px; flex:0 0 38px; object-fit:cover; border-radius:.35rem; background:var(--mo-surface-3); }

/* --- /sets/ mobile card reflow (<lg the table stacks into rows) -------------- */
.mo-set-card {
  display:block; padding:.6rem .7rem; text-decoration:none; color:var(--mo-text);
  border:1px solid var(--mo-border); border-radius:.6rem; background:var(--mo-surface-2);
}
.mo-set-card + .mo-set-card { margin-top:.4rem; }
.mo-set-card:hover, .mo-set-card:focus-visible { border-color:var(--mo-accent); background:var(--mo-surface-3); }
.mo-set-card-title { font-weight:600; line-height:1.2; }
.mo-set-card-meta { font-size:.8rem; color:var(--mo-text-muted); }

/* --- illustrated empty state -------------------------------------------------- */
.mo-set-empty { text-align:center; padding:2.5rem 1rem; color:var(--mo-text-muted); }
.mo-set-empty .mo-aviary { opacity:.85; filter:drop-shadow(0 6px 14px rgba(0,0,0,.14)); margin-bottom:.6rem; }

/* --- /set/<id>/ provenance signal-chain block ------------------------------- */
.mo-prov {
  border:1px solid var(--mo-border); border-radius:.6rem;
  background:var(--mo-surface-2); padding:.7rem .85rem;
}
.mo-prov-title { font-size:.7rem; text-transform:uppercase; letter-spacing:.06em; color:var(--mo-text-muted); display:flex; align-items:center; gap:.35rem; margin-bottom:.5rem; }
.mo-prov-grid { display:grid; grid-template-columns:max-content 1fr; gap:.3rem .75rem; align-items:baseline; }
.mo-prov-grid dt { font-size:.74rem; color:var(--mo-text-muted); text-transform:uppercase; letter-spacing:.03em; margin:0; }
.mo-prov-grid dd { margin:0; font-size:.9rem; }
/* the lineage rendered as a signal chain: tokens split on > with chevrons */
.mo-chain { display:flex; flex-wrap:wrap; align-items:center; gap:.25rem .15rem; }
.mo-chain-node {
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size:.78rem;
  padding:.05rem .4rem; border-radius:.35rem;
  background:var(--mo-surface-3); border:1px solid var(--mo-border); color:var(--mo-text);
}
.mo-chain-arrow { color:var(--mo-accent); font-size:.8rem; }

/* --- /set/<id>/ cross-link rail --------------------------------------------- */
.mo-xlinks { display:flex; flex-wrap:wrap; gap:.4rem; }
.mo-xlink {
  display:inline-flex; align-items:center; gap:.3rem; text-decoration:none;
  font-size:.8rem; padding:.22rem .55rem; border-radius:1rem;
  background:var(--mo-surface-2); border:1px solid var(--mo-border); color:var(--mo-text);
  transition:border-color .12s ease, background .12s ease;
}
.mo-xlink:hover, .mo-xlink:focus-visible { border-color:var(--mo-accent); background:var(--mo-surface-3); color:var(--mo-text); }
.mo-xlink .bi { color:var(--mo-accent); }

/* --- archive Request CTA — a clear call to action, not a dead button -------- */
.mo-set-request {
  display:inline-flex; align-items:center; gap:.4rem;
  font-weight:600; padding:.4rem .9rem; border-radius:.5rem;
  color:#4a3503; background:#fbeec2; border:1px solid #e6cf73; text-decoration:none;
  transition:filter .12s ease;
}
.mo-set-request:hover, .mo-set-request:focus-visible { filter:brightness(.96); color:#4a3503; }
[data-bs-theme="dark"] .mo-set-request { color:#fbeec2; background:rgba(230,207,115,.16); border-color:rgba(230,207,115,.45); }
