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

    body {
      width: 100vw;
      min-height: 100vh;
      display: flex;
      flex-direction: column;
      /* Allow the page to scroll vertically when the sequence display
         grows past the viewport — chip rows expand to show every step
         instead of internal scrolling inside .sequence-display. The
         master-scope is position:fixed so it stays visible regardless
         of how far the user scrolls. */
      overflow-y: auto;
      overflow-x: hidden;
      -webkit-overflow-scrolling: touch;
    }

    @media (max-width: 700px) {
      /* Mobile body rules now match the desktop ones (the base body
         already does min-height: 100vh + overflow-y: auto), so this
         block only carries mobile-only overrides for inner sizing. */
      .grid,
      #xy-pad {
        flex: none;
        grid-auto-rows: minmax(70px, 1fr);
        min-height: 55vh;
      }
      .grid-control-row2 {
        flex-basis: 100%;
      }
      /* Grid panel needs to wrap so its rows of controls fit a narrow
         viewport without overflowing the side. */
      .grid-settings-panel {
        width: calc(100vw - 16px);
        max-width: calc(100vw - 16px);
      }
      .grid-control { flex-wrap: wrap; }
      .grid-control select#scale-select { width: 100%; min-width: 90px; }
      .grid-control input[type=range] { flex: 1 1 80px; min-width: 60px; width: auto; }
    }

    /* MENU BAR */
    .menubar {
      background: #000;
      border-bottom: 3px solid #000;
      display: flex;
      align-items: center;
      flex-wrap: nowrap;
      padding: 10px 8px;
      gap: 6px;
      position: relative;
      overflow-x: hidden;
    }
    /* Hide the menubar entirely when no lane is collapsed. The
       dropdown panels (project, sounds, fx, etc.) have already been
       reparented to <body> by detachPanelsFromMenubar at init, so
       hiding the menubar doesn't touch their open / close behavior.
       :has() lets the rule re-evaluate the moment renderSequence
       removes the [hidden] attribute on the menubar-lanes slot. */
    .menubar:has(.menubar-lanes[hidden]) { display: none; }
    /* Voices / Project / Edit grow to share any leftover row space so
       the row stays full when the master-vol slider is capped. They
       still won't shrink below their content (flex-shrink: 0) so the
       labels stay legible; the project-name label absorbs the squeeze
       on narrow viewports. */
    .menubar > #voices-menu-btn,
    .menubar > #project-menu-btn,
    .menubar > #edit-mode-btn {
      flex: 1 0 auto;
    }
    .menubar > .project-name-label {
      min-width: 0;
      flex-shrink: 1;
    }
    .menubar > .master-vol {
      min-width: 0;
      /* Capped at ~25vw so the slider doesn't hog the row. The other
         menubar items grow to fill whatever space the slider gives up —
         rule #1 (no horizontal scrolling, fill the row). */
      flex: 1 1 auto;
      max-width: 25vw;
    }
    .menubar > .master-vol input[type="range"] {
      min-width: 0;
      width: 100%;
      flex: 1 1 0;
    }
    /* Tighten button padding on narrow viewports so all four items fit
       without clipping Edit. */
    @media (max-width: 480px) {
      .menubar {
        padding: 10px 6px;
        gap: 4px;
      }
      .menubar > #voices-menu-btn,
      .menubar > #project-menu-btn,
      .menubar > #edit-mode-btn {
        padding: 6px 8px;
        font-size: 0.72rem;
      }
    }

    /* Menubar lane slot — every collapsed lane (single or many)
       parks here so the editor area below stays focused on the
       expanded lanes. Holds a single .lane-row-collapsed-strip that
       lays its lane rows out horizontally and wraps when there are
       too many to fit. Hidden when no lane is collapsed so the
       menubar doesn't grow for nothing. */
    .menubar-lanes {
      display: block;
      width: 100%;
      padding: 4px 8px;
      background: #050510;
      border-bottom: 1px solid #1a1a2e;
    }
    .menubar-lanes[hidden] { display: none; }
    .menubar-lanes .lane-row-collapsed-strip {
      margin: 0;
    }

    /* Project bar — sits between the saved-sequence bank and the
       master-scope waveform. Hosts the Project ▾ button + the active
       project name label. The Project panel itself lives back up in
       the menubar; pinPanelToButton positions it absolutely under the
       button when opened. */
    .project-bottom-bar {
      display: flex;
      flex-direction: row;
      align-items: center;
      gap: 10px;
      padding: 8px 16px;
      background: #050510;
      border-top: 1px solid #1a1a2e;
      border-bottom: 1px solid #1a1a2e;
      /* Reserve enough height so the Project button + label are
         visibly centred. Without a min-height, the bar collapsed to
         exactly button height + 16 px padding, and the button looked
         flush with the top edge because the project-name-label below
         it picked up a smaller intrinsic line height. */
      min-height: 48px;
      line-height: 1;
    }
    .project-bottom-bar #project-menu-btn {
      flex: 0 0 auto;
      align-self: center;
    }
    .project-bottom-bar .project-name-label {
      flex: 1 1 auto;
      min-width: 0;
      overflow: hidden;
      text-overflow: ellipsis;
      white-space: nowrap;
      align-self: center;
      color: #a0aec0;
      font-family: 'Segoe UI', sans-serif;
      font-size: 0.78rem;
    }

    /* Master volume + Note Edit pill — moved into the Sounds panel.
       Lays them out on a single row that mirrors the .sounds-submenus
       chrome. Slider stretches; the edit button stays a fixed pill on
       the right. */
    .grid-settings-panel .sounds-utilities {
      display: flex;
      flex-direction: row;
      align-items: center;
      /* Center the Pitch bend + Note edit pair horizontally in the
         row so they sit symmetrically rather than left-aligned. */
      justify-content: center;
      gap: 10px;
    }
    .grid-settings-panel .sounds-utilities .master-vol {
      flex: 1 1 auto;
      display: flex;
      align-items: center;
      gap: 6px;
      min-width: 0;
    }
    .grid-settings-panel .sounds-utilities .master-vol input[type="range"] {
      flex: 1 1 auto;
      min-width: 0;
      width: 100%;
    }
    .grid-settings-panel .sounds-utilities .master-vol-icon {
      font-size: 0.9rem;
      flex: 0 0 auto;
    }
    .grid-settings-panel .sounds-utilities #edit-mode-btn {
      flex: 0 0 auto;
    }
    /* Radial Pitch Bend toggle, when housed inside the Sounds panel,
       drops the banner-row right border and matches the pill chrome
       of its row-mates. The body.radial-tone active style (purple
       gradient) still wins via specificity so the on-state stays
       legible. */
    .grid-settings-panel .sounds-utilities #radial-tone-btn {
      flex: 0 0 auto;
      border: 1px solid #2d2d3f;
      border-radius: 6px;
      padding: 4px 10px;
      font-size: 0.78rem;
      color: #cbd5e0;
    }
    .grid-settings-panel .sounds-utilities #radial-tone-btn:hover:not(:disabled) {
      border-color: #9f7aea;
      color: #d6bcfa;
    }

    /* ± note-shift buttons — − sits to the left of the Sounds banner
       and + sits to the right. Both are 15% wider than the original
       compact pill chrome (12 px → ~14 px horizontal padding +
       min-width bump) so they're easier to hit on mobile.
       Border layout puts a 1 px separator only on the edge that
       FACES the lane chrome — never on the edge that touches the
       Sounds button — so both ± buttons visually fuse into the
       Sounds menu the same way. */
    .note-shift-btn {
      flex: 0 0 auto;
      min-width: 28px;
      padding: 0 14px;
      background: transparent;
      border: none;
      color: #a0aec0;
      font-family: 'Segoe UI', sans-serif;
      font-size: 0.9rem;
      font-weight: 700;
      line-height: 1;
      cursor: pointer;
      user-select: none;
      transition: background 0.15s ease, color 0.15s ease;
    }
    #note-shift-down { border-left:  1px solid #1a1a2e; }
    #note-shift-up   { border-right: 1px solid #1a1a2e; }
    /* Strip the Sounds banner's right border when a note-shift +
       button follows it (i.e., always, in the current banner-row
       layout). Combined with the missing border-left on + above,
       the seam between Sounds and + vanishes — matching the way −
       already blends into Sounds on the other side. */
    .banner-row #scale-banner-half { border-right: none; }
    @media (hover: hover) {
      .note-shift-btn:hover { background: #0a0a14; color: #81e6d9; }
    }

    .tone-panel {
      display: none;
      flex-direction: column;
      gap: 6px;
      position: absolute;
      top: calc(100% + 4px);
      right: 10px;
      background: #1a1a2e;
      border: 1px solid #2d2d3f;
      border-radius: 10px;
      padding: 10px;
      z-index: 9000;
      box-shadow: 0 8px 24px rgba(0,0,0,0.5);
      min-width: 160px;
      max-height: calc(100vh - 80px);
      overflow-y: auto;
    }
    .tone-panel.open { display: flex; }
    .tone-opt {
      padding: 6px 14px;
      border: 1px solid #2d2d3f;
      border-radius: 8px;
      background: transparent;
      color: #a0aec0;
      font-family: 'Segoe UI', sans-serif;
      font-size: 0.85rem;
      font-weight: 600;
      cursor: pointer;
      text-align: left;
      transition: all 0.15s ease;
    }
    .tone-opt:hover { border-color: #4299e1; color: #4299e1; }
    /* Family rows — top-level entries that drill down into a sub-list. */
    .tone-opt.tone-opt-family {
      display: flex;
      justify-content: space-between;
      align-items: center;
    }
    .tone-opt-count {
      font-size: 0.7rem;
      font-weight: 500;
      color: #4a4a6a;
      letter-spacing: 0.5px;
    }
    .tone-opt.tone-opt-family:hover .tone-opt-count { color: #4299e1; }
    /* Subgroup label — used inside a family's drilldown to group its
       members (e.g., Drums splits into Single drums vs Drum kits). */
    .tone-subhead {
      font-family: 'Segoe UI', sans-serif;
      font-size: 0.65rem;
      font-weight: 700;
      letter-spacing: 1px;
      text-transform: uppercase;
      color: #4a4a6a;
      padding: 6px 4px 2px;
    }
    /* Back row — returns from a family drilldown to the top family list. */
    .tone-opt.tone-opt-back {
      color: #4a4a6a;
      font-size: 0.78rem;
      letter-spacing: 0.5px;
    }
    .tone-opt.tone-opt-back:hover {
      border-color: #4299e1;
      color: #a0aec0;
    }
    /* "Custom" indicator — non-clickable marker at the top of the panel
       when at least one cell's tone has been changed away from the
       global default. Pinkish accent + italic so it reads as a current
       state, not as a selectable option. */
    .tone-opt.tone-opt-custom {
      border-color: #f472b6;
      color: #f9a8d4;
      background: rgba(244, 114, 182, 0.08);
      font-style: italic;
      cursor: default;
    }
    .tone-opt.tone-opt-custom:hover {
      border-color: #f472b6;
      color: #f9a8d4;
    }

    /* Global FX panel — three slider rows for reverb / delay / distortion.
       Same panel chrome as the tone dropdown so the menubar feels uniform. */
    .fx-panel {
      display: none;
      flex-direction: column;
      gap: 10px;
      position: absolute;
      top: calc(100% + 4px);
      right: 10px;
      background: #1a1a2e;
      border: 1px solid #2d2d3f;
      border-radius: 10px;
      padding: 12px 14px;
      z-index: 9000;
      box-shadow: 0 8px 24px rgba(0,0,0,0.5);
      min-width: 220px;
      max-height: calc(100vh - 80px);
      overflow-y: auto;
    }
    .fx-panel.open { display: flex; }
    /* Per-lane scope label — shows which lane these sliders edit so
       there's no confusion about where changes land. Updated by the
       FX panel's refreshFxSliderUI on every activeLaneChanged event. */
    .fx-panel-scope {
      font-family: 'Segoe UI', sans-serif;
      font-size: 0.78rem;
      font-weight: 700;
      letter-spacing: 0.5px;
      color: #4fd1c5;
      background: rgba(79, 209, 197, 0.10);
      border: 1px solid rgba(79, 209, 197, 0.30);
      border-radius: 6px;
      padding: 6px 10px;
      margin-bottom: 4px;
      text-align: center;
    }
    .fx-control { display: flex; flex-direction: column; gap: 4px; }
    .fx-control-row {
      display: flex; justify-content: space-between; align-items: baseline;
      font-family: 'Segoe UI', sans-serif; font-size: 0.75rem;
      color: #a0aec0;
    }
    .fx-val { color: #e2e8f0; font-weight: 600; }
    .fx-control input[type=range] { width: 100%; accent-color: #4299e1; }
    .fx-group-label {
      font-family: 'Segoe UI', sans-serif;
      font-size: 0.66rem;
      font-weight: 700;
      letter-spacing: 1px;
      text-transform: uppercase;
      color: #4a4a6a;
      margin: 4px 0 -2px;
    }
    .fx-group-label:first-child { margin-top: 0; }
    /* Each FX section header now hosts a small ON/OFF toggle pinned to the
       right — per-effect bypass without nuking the configured mix value. */
    .fx-group-label.fx-group-head {
      display: flex;
      align-items: center;
      justify-content: space-between;
      gap: 10px;
    }
    .fx-bypass {
      padding: 1px 8px;
      border: 1px solid #2d2d3f;
      border-radius: 9px;
      background: transparent;
      color: #68d391;
      font-family: 'Segoe UI', sans-serif;
      font-size: 0.6rem;
      font-weight: 700;
      letter-spacing: 1px;
      cursor: pointer;
      text-transform: uppercase;
      transition: all 0.15s ease;
    }
    .fx-bypass:hover { border-color: #38a169; }
    .fx-bypass.off {
      color: #4a4a6a;
      border-color: #2d2d3f;
    }
    .fx-bypass.off:hover { border-color: #4a4a6a; color: #6b7280; }
    .fx-actions { display: flex; justify-content: flex-end; margin-top: 4px; }
    .fx-order-hint {
      color: #4a4a6a; font-size: 0.7rem; font-weight: 500;
      margin-left: 8px; letter-spacing: 0.4px;
    }
    .fx-order { display: flex; flex-direction: column; gap: 4px; margin-bottom: 8px; }
    .fx-order-row {
      display: flex; align-items: center; gap: 6px;
      padding: 4px 8px;
      background: #1a1a2e; border: 1px solid #2d2d3f; border-radius: 6px;
      color: #a0aec0; font-size: 0.8rem; font-weight: 600;
    }
    .fx-order-row .fx-order-name { flex: 1 1 auto; }
    .fx-order-row .fx-order-pos {
      color: #4a4a6a; font-size: 0.7rem; min-width: 18px;
    }
    .fx-order-row .fx-order-arrow {
      width: 24px; height: 22px;
      background: transparent; border: 1px solid #2d2d3f; border-radius: 4px;
      color: #a0aec0; cursor: pointer; padding: 0;
      font-family: inherit; font-size: 0.85rem;
      display: inline-flex; align-items: center; justify-content: center;
    }
    .fx-order-row .fx-order-arrow:hover:not(:disabled) {
      border-color: #4fd1c5; color: #4fd1c5;
    }
    .fx-order-row .fx-order-arrow:disabled { opacity: 0.3; cursor: not-allowed; }
    .fx-reset {
      padding: 4px 12px;
      border: 1px solid #2d2d3f;
      border-radius: 12px;
      background: transparent;
      color: #a0aec0;
      font-family: 'Segoe UI', sans-serif;
      font-size: 0.72rem;
      font-weight: 600;
      cursor: pointer;
      transition: all 0.15s ease;
    }
    .fx-reset:hover { border-color: #4299e1; color: #4299e1; }

    .grid-settings-panel {
      display: none;
      flex-direction: column;
      gap: 10px;
      position: absolute;
      top: calc(100% + 4px);
      right: 10px;
      /* Subtle vertical gradient + teal-tinted accent so the panel
         reads as its own "Sounds" space rather than a generic
         dropdown. Cyan/violet shadow gives it a hint of motion. */
      background:
        linear-gradient(180deg, #1f1f33 0%, #15152a 60%, #10101c 100%);
      border: 1px solid #2f3550;
      border-radius: 12px;
      padding: 14px;
      z-index: 9000;
      box-shadow:
        0 18px 48px rgba(0, 0, 0, 0.55),
        0 0 24px rgba(79, 209, 197, 0.10);
      max-width: calc(100vw - 20px);
      max-height: calc(100vh - 80px);
      overflow-y: auto;
      box-sizing: border-box;
    }
    .grid-settings-panel.open { display: flex; }
    /* When the active lane is in Graph mode, .grid-only rows / labels
       hide so the Sounds panel only shows controls that apply to the
       XY pad. .xy-settings handles its own reverse case via the
       existing body.fluid-grid .xy-settings { display: flex } rule. */
    body.fluid-grid .grid-settings-panel .grid-only { display: none !important; }

    .menubar a {
      color: #a0aec0;
      text-decoration: none;
      font-family: 'Segoe UI', sans-serif;
      font-size: 0.9rem;
      font-weight: 600;
      transition: color 0.2s;
      margin-right: 4px;
    }
    .menubar a:hover { color: #4299e1; }

    .menubar-toggle {
      margin-left: auto;
      display: flex;
      align-items: center;
      gap: 6px;
      color: #a0aec0;
      font-family: 'Segoe UI', sans-serif;
      font-size: 0.78rem;
      font-weight: 600;
      cursor: pointer;
      user-select: none;
    }
    .menubar-toggle input { cursor: pointer; }

    .menu-btn {
      width: 38px;
      height: 38px;
      min-width: 38px;
      min-height: 38px;
      border: none;
      border-radius: 50%;
      cursor: pointer;
      font-size: 1rem;
      display: flex;
      align-items: center;
      justify-content: center;
      transition: all 0.15s ease;
      flex-shrink: 0;
    }

    #play-btn {
      background: #000;
      color: #00bfff;
      border: 2px solid #00bfff;
      box-shadow: 0 0 12px rgba(0, 191, 255, 0.45);
    }
    #play-btn:hover {
      transform: translateY(-1px);
      box-shadow: 0 0 18px rgba(0, 191, 255, 0.7);
    }


    .tempo-control {
      display: flex;
      align-items: center;
      gap: 8px;
      color: #a0aec0;
      font-family: 'Segoe UI', sans-serif;
      font-size: 0.85rem;
      flex-shrink: 0;
    }

    /* Transport cluster — Play, Rec, Loop (and Drift in Poly mode)
       fill the row evenly. Clear and Riff have moved to their own row
       above the step controls. In Mono only three buttons live here;
       in Poly the Drift button takes a fourth slot, so Rec and Loop
       shrink proportionally to keep the row on a single line. */
    /* Master-bus oscilloscope. Pinned to the very bottom of the viewport
       as a full-width status strip. body gets a matching padding-bottom
       so the footer transport and saved-viewer aren't covered. The
       canvas buffer width is synced to clientWidth in the draw loop
       (bloops.html initMasterScope) so the waveform stays crisp at any
       viewport width without per-frame canvas.width churn. */
    body { padding-bottom: 40px; }
    .master-scope {
      position: fixed;
      left: 0;
      right: 0;
      bottom: 0;
      width: 100vw;
      height: 40px;
      background: #050510;
      border-top: 1px solid #1a1a2e;
      border-radius: 0;
      margin: 0;
      display: block;
      z-index: 100;
    }
    .footer-transport {
      display: flex;
      align-items: stretch;
      gap: 10px;
      flex: 1 1 100%;
      flex-wrap: nowrap;
    }
    .footer-transport > #rec-btn,
    .footer-transport > #loop-btn,
    .footer-transport > #audio-reset-btn {
      flex: 1 1 0;
      min-width: 0;
      height: 52px;
      font-size: 1rem;
      padding: 0 18px;
      white-space: nowrap;
      overflow: hidden;
      text-overflow: ellipsis;
    }
    /* Poly mode: tighten Rec / Loop padding + font so the pills + Play
       circle fit on the row without overflow. */
    .footer-transport.poly-mode-on > #rec-btn,
    .footer-transport.poly-mode-on > #loop-btn,
    .footer-transport.poly-mode-on > #audio-reset-btn {
      padding: 0 10px;
      font-size: 0.92rem;
    }
    /* Reset-Audio pill — neutral by default; .warn turns it amber when
       schedulerTick detects measurable slip (>25ms); .alarm turns it red
       when slip crosses ~80ms. driven by refreshAudioResetUI. */
    #audio-reset-btn {
      border: 1px solid #2d2d3f;
      border-radius: 20px;
      background: transparent;
      color: #a0aec0;
      font-family: 'Segoe UI', sans-serif;
      font-weight: 600;
      cursor: pointer;
      transition: all 0.18s ease;
      touch-action: manipulation;
    }
    @media (hover: hover) {
      #audio-reset-btn:hover { border-color: #4299e1; color: #90cdf4; }
    }
    #audio-reset-btn.warn {
      border-color: #ed8936;
      color: #fbd38d;
      background: rgba(237, 137, 54, 0.12);
    }
    /* Mobile: footer-transport packs Play (circle, 52×52) + Rec + Loop
       + Audio (and Drift in poly) onto one row. At ~375px Audio's full
       label crowds out the others. Tighten padding + font + shorten
       its label to just "↻". The visible-state styling (warn/alarm
       glow, colors) is unchanged. */
    @media (max-width: 600px) {
      .footer-transport > #audio-reset-btn {
        padding: 0 8px;
        font-size: 0;          /* hide the "↻ Audio" text */
        position: relative;
      }
      .footer-transport > #audio-reset-btn::before {
        content: '↻';
        font-size: 1.2rem;
        font-weight: 700;
        line-height: 1;
      }
    }
    #audio-reset-btn.alarm {
      border-color: #fc8181;
      color: #fed7d7;
      background: rgba(229, 62, 62, 0.18);
      box-shadow: 0 0 12px rgba(252, 129, 129, 0.4);
      animation: audioResetPulse 1.1s ease-in-out infinite;
    }
    @keyframes audioResetPulse {
      0%, 100% { opacity: 1; }
      50% { opacity: 0.7; }
    }
    .footer-transport > #play-btn {
      width: 52px;
      height: 52px;
      min-width: 52px;
      min-height: 52px;
      font-size: 1.25rem;
      flex: 0 0 auto;
    }
    /* Drift option inside the Riff dropdown. Inherits the riff-panel
       button base style; the orange identity only kicks in when the
       active lane is actively drifting or locked (refreshDriftBtn adds
       .active in those states). */
    .riff-panel > #drift-btn.active {
      border-color: #ed8936;
      color: #fbd38d;
      background: rgba(237, 137, 54, 0.18);
      box-shadow: 0 0 12px rgba(237, 137, 54, 0.4);
    }
    @media (hover: hover) {
      .riff-panel > #drift-btn:hover:not(:disabled) { border-color: #ed8936; color: #fbd38d; }
    }
    /* Drifting lane — orange-tinted glow on the lane row so the user
       can see at a glance which lanes are drifting. */
    .lane-row.drifting {
      box-shadow: inset 0 0 0 1px rgba(237, 137, 54, 0.55);
    }
    .lane-row.drifting.active {
      box-shadow: inset 0 0 0 1px #ed8936, 0 0 12px rgba(237, 137, 54, 0.35);
    }
    .lane-row.drifting .lane-label::after {
      content: ' ⤳';
      color: #ed8936;
      font-weight: 700;
    }

    /* Row that hosts Clear + Riff between the sequence chip display and
       the step controls. Each button takes half the row. */
    .clear-riff-row {
      display: flex;
      gap: 10px;
      padding: 6px 18px;
      background: #050510;
      border-bottom: 1px solid #1a1a2e;
      /* Sequence-display sits above this bar and may want to grow tall.
         flex-shrink:0 reserves this row's natural height so it never
         collapses or gets visually overlapped by chip overflow. */
      flex-shrink: 0;
    }
    .clear-riff-row > #save-btn,
    .clear-riff-row > #clear-btn,
    .clear-riff-row > #poly-mode-btn,
    .clear-riff-row > .riff-wrapper {
      flex: 1 1 0;
      min-width: 0;
    }
    .clear-riff-row > #save-btn,
    .clear-riff-row > #clear-btn,
    .clear-riff-row > #poly-mode-btn {
      width: 100%;
    }
    .clear-riff-row > .riff-wrapper > #riff-menu-btn {
      width: 100%;
    }
    /* Tighten horizontal padding inside the row so all four pills fit
       on a single line without the Riff carrot (▾) wrapping. */
    .clear-riff-row > #save-btn,
    .clear-riff-row > #clear-btn,
    .clear-riff-row > #poly-mode-btn,
    .clear-riff-row > .riff-wrapper > #riff-menu-btn {
      padding-left: 6px;
      padding-right: 6px;
      white-space: nowrap;
      overflow: hidden;
      text-overflow: ellipsis;
    }
    #tempo-slider {
      width: 90px;
      min-width: 60px;
      accent-color: #00bfff;
      flex-shrink: 1;
      touch-action: manipulation;
    }
    /* Kill the iOS 300ms double-tap-to-zoom delay on every interactive
       button in the workspace — KEEP/Wrap/REST, the size chips, the
       transport pills, the Save/Clear/Riff row, the Free/Fixed pill,
       and the saved-sequence bank. touch-action: manipulation tells
       the browser the gesture is a tap, so the click event fires on
       pointerup with no synthetic delay. */
    #play-lock-btn,
    #chord-btn,
    #rest-bar,
    #play-btn,
    #rec-btn,
    #loop-btn,
    #clear-btn,
    #save-btn,
    #riff-menu-btn,
    #step-mode-btn,
    #clear-bank-btn,
    .size-chip,
    .riff-panel button,
    .saved-block {
      touch-action: manipulation;
    }
    @media (hover: none) and (pointer: coarse) {
      /* Bigger thumb + taller hit area so a fingertip can grab and drag the
         slider on touch devices. iOS in particular makes the native thumb
         too small to scrub at 90px width. */
      #tempo-slider {
        height: 32px;
        -webkit-appearance: none;
        appearance: none;
        background: transparent;
      }
      #tempo-slider::-webkit-slider-runnable-track {
        height: 4px;
        background: #2d2d3f;
        border-radius: 2px;
      }
      #tempo-slider::-moz-range-track {
        height: 4px;
        background: #2d2d3f;
        border-radius: 2px;
      }
      #tempo-slider::-webkit-slider-thumb {
        -webkit-appearance: none;
        appearance: none;
        width: 20px;
        height: 20px;
        border-radius: 50%;
        background: var(--bpm-thumb, #00bfff);
        margin-top: -8px;
        border: none;
      }
      #tempo-slider::-moz-range-thumb {
        width: 20px;
        height: 20px;
        border-radius: 50%;
        background: var(--bpm-thumb, #00bfff);
        border: none;
      }
    }
    #tempo-input {
      width: 52px;
      background: #1a1a2e;
      border: 1px solid #2d2d3f;
      border-radius: 6px;
      color: #e2e8f0;
      font-size: 0.85rem;
      font-family: 'Segoe UI', sans-serif;
      padding: 3px 6px;
      text-align: center;
    }
    #tempo-input:focus { outline: none; border-color: #00bfff; }
    /* 3-digit BPM picker — three buttons, each opening a 0–9 picker on
       click. The legacy +/- step buttons (.bpm-step) and per-column
       wrappers (.bpm-digit-col) are gone; .bpm-digit is now a button
       you press to bring up the value picker. */
    /* Metronome toggle — sits to the LEFT of the BPM digits and emits
       an audible click at the current BPM when active. Off = dim grey,
       on = bright cyan with a subtle glow that pulses with each tick. */
    #metronome-btn {
      width: 26px;
      height: 26px;
      padding: 0;
      margin-right: 6px;
      border: 1px solid #2d2d3f;
      border-radius: 50%;
      background: #050510;
      color: #4a4a6a;
      font-family: 'Segoe UI', sans-serif;
      font-size: 0.95rem;
      line-height: 1;
      cursor: pointer;
      transition: all 0.15s ease;
      flex-shrink: 0;
      touch-action: manipulation;
    }
    @media (hover: hover) {
      #metronome-btn:hover { border-color: #4fd1c5; color: #81e6d9; }
    }
    #metronome-btn.active {
      border-color: #4fd1c5;
      color: #81e6d9;
      box-shadow: 0 0 12px rgba(79, 209, 197, 0.45);
    }
    #metronome-btn.tick { transform: scale(1.18); }

    /* On mobile, float the metronome button to the midpoint between
       "Mercy Wizard" (absolute-centered at 50%) and the BPM picker
       (pinned to the right edge of the home-bar-row). Absolute
       positioning anchors to the .home-bar-row's sticky containing
       block, skipping its .tempo-control flex parent. The button keeps
       its tempoSlider/tempoInput wiring because we only move it
       visually — it's still a child of .tempo-control in the DOM. */
    @media (max-width: 700px) {
      .home-bar-row #metronome-btn {
        position: absolute;
        left: 75%;
        top: 50%;
        transform: translate(-50%, -50%);
        margin: 0;
        z-index: 1;
      }
      .home-bar-row #metronome-btn.tick {
        transform: translate(-50%, -50%) scale(1.18);
      }
    }

    .bpm-digits {
      display: flex;
      align-items: center;
      gap: 4px;
      flex-shrink: 0;
    }
    .bpm-digit {
      min-width: 22px;
      padding: 2px 0;
      text-align: center;
      font-family: 'Segoe UI', sans-serif;
      font-size: 1rem;
      font-weight: 800;
      letter-spacing: 0.5px;
      cursor: pointer;
      /* Static teal palette to match the +/- step buttons next to the
         digits. The flashing animation is opt-in (host gets
         .bpm-flashing); without it the digits sit calmly in the
         neutral teal. */
      color: #81e6d9;
      background: #050510;
      border: 1px solid #4fd1c5;
      border-radius: 4px;
      user-select: none;
      text-shadow: 0 0 6px rgba(79, 209, 197, 0.45);
    }
    .bpm-digits.bpm-flashing .bpm-digit {
      animation: bpm-pulse var(--bpm-pulse-dur, 0.5s) ease-in-out infinite;
    }
    @keyframes bpm-pulse {
      /* Peak at 0% / 100% so each cycle BEGINS with the red glow — that
         way restarting the animation at note-attack time makes the
         visual peak land on the audible beat. The midpoint dips to
         fully transparent for the strong on-off oscillation. */
      0%, 100% {
        opacity: 1;
        color: #ff4848;
        text-shadow:
          0 0 8px #ff2a2a,
          0 0 16px rgba(255, 40, 40, 0.75),
          0 0 26px rgba(255, 0, 0, 0.5);
      }
      50% {
        opacity: 0;
        color: var(--bpm-thumb, #00bfff);
        text-shadow: none;
      }
    }
    @media (prefers-reduced-motion: reduce) {
      .bpm-digits.bpm-flashing .bpm-digit { animation: none; }
    }
    .bpm-label {
      font-family: 'Segoe UI', sans-serif;
      font-size: 0.72rem;
      font-weight: 700;
      letter-spacing: 1px;
      text-transform: uppercase;
      color: #a0aec0;
      margin-left: 4px;
    }
    .tempo-unit {
      color: #a0aec0;
      font-size: 0.8rem;
      font-family: 'Segoe UI', sans-serif;
    }

    .grid-control {
      display: flex;
      align-items: center;
      gap: 6px;
      flex-wrap: wrap;
      color: #a0aec0;
      font-family: 'Segoe UI', sans-serif;
      font-size: 0.8rem;
      flex-shrink: 0;
    }
    .grid-control label {
      display: flex;
      align-items: center;
      gap: 4px;
    }
    .grid-control input[type=number],
    .grid-control select {
      width: 54px;
      background: #1a1a2e;
      border: 1px solid #2d2d3f;
      border-radius: 6px;
      color: #e2e8f0;
      font-size: 0.8rem;
      font-family: 'Segoe UI', sans-serif;
      padding: 3px 6px;
      text-align: center;
    }
    .grid-control input[type=number]:focus,
    .grid-control select:focus { outline: none; border-color: #4299e1; }
    .grid-control select#scale-select { width: 110px; }
    .grid-control input[type=range] {
      width: 80px;
      accent-color: #4299e1;
    }

    .cell.out-of-scale {
      background: #000 !important;
      pointer-events: none;
    }
    .cell.out-of-scale span,
    .cell.out-of-scale .cell-sound-select { visibility: hidden; }

    /* Colors menu lives in the seq-grid-bar — match the teal pill look
       of the Step Mode toggle next to it for visual consistency. */
    #colors-menu-btn {
      padding: 3px 8px;
      border: 1px solid #4fd1c5;
      border-radius: 14px;
      background: transparent;
      color: #81e6d9;
      font-family: 'Segoe UI', sans-serif;
      font-size: 0.7rem;
      font-weight: 600;
      cursor: pointer;
      transition: all 0.15s ease;
    }
    #colors-menu-btn:hover { border-color: #81e6d9; color: #b2f5ea; }
    #colors-menu-btn.open  {
      border-color: #81e6d9;
      color: #b2f5ea;
      background: rgba(79, 209, 197, 0.18);
      box-shadow: 0 0 12px rgba(79, 209, 197, 0.4);
    }

    .colors-panel {
      display: none;
      flex-direction: column;
      gap: 6px;
      position: absolute;
      top: calc(100% + 4px);
      left: 10px;
      background: #1a1a2e;
      border: 1px solid #2d2d3f;
      border-radius: 10px;
      padding: 10px;
      z-index: 9000;
      box-shadow: 0 8px 24px rgba(0,0,0,0.5);
      min-width: 160px;
    }
    .colors-panel.open { display: flex; }
    .colors-opt {
      padding: 6px 14px;
      border: 1px solid #2d2d3f;
      border-radius: 8px;
      background: transparent;
      color: #a0aec0;
      font-family: 'Segoe UI', sans-serif;
      font-size: 0.85rem;
      font-weight: 600;
      cursor: pointer;
      text-align: left;
      transition: all 0.15s ease;
    }
    .colors-opt:hover { border-color: #4299e1; color: #4299e1; }

    /* Voices menu — same pill + panel styling as Colors, listing saved
       Voices (note grid presets) fetched from Drive on open. */
    #voices-menu-btn {
      padding: 6px 14px;
      border: 1px solid #2d2d3f;
      border-radius: 20px;
      background: transparent;
      color: #a0aec0;
      font-family: 'Segoe UI', sans-serif;
      font-size: 0.8rem;
      font-weight: 600;
      cursor: pointer;
      transition: all 0.15s ease;
    }
    #voices-menu-btn:hover { border-color: #4299e1; color: #4299e1; }
    #voices-menu-btn.open  { border-color: #4299e1; color: #4299e1; background: rgba(66,153,225,0.12); }

    /* Project menu — same pill + panel chrome as Colors / Voices, holds the
       New / Save / Load actions formerly scattered across the page. */
    #project-menu-btn {
      padding: 6px 14px;
      border: 1px solid #2d2d3f;
      border-radius: 20px;
      background: transparent;
      color: #a0aec0;
      font-family: 'Segoe UI', sans-serif;
      font-size: 0.8rem;
      font-weight: 600;
      cursor: pointer;
      transition: all 0.15s ease;
    }
    #project-menu-btn:hover { border-color: #4299e1; color: #4299e1; }
    #project-menu-btn.open  { border-color: #4299e1; color: #4299e1; background: rgba(66,153,225,0.12); }
    /* Edit toggle — sits to the right of master-vol in the menubar. When
       on, body gets .edit-mode and per-cell sound-editor carrots become
       visible. Default is off so the grid stays clean for play; users
       click Edit to surface the carrots when they want to tweak sounds. */
    #edit-mode-btn {
      padding: 6px 14px;
      border: 1px solid #2d2d3f;
      border-radius: 20px;
      background: transparent;
      color: #a0aec0;
      font-family: 'Segoe UI', sans-serif;
      font-size: 0.8rem;
      font-weight: 600;
      cursor: pointer;
      transition: all 0.15s ease;
    }
    /* Hover-only highlight kept inside @media (hover: hover) so a tap
       on mobile doesn't stick the button in the teal state — without
       this guard, mobile browsers retain :hover after release and the
       button kept looking "on" even after the user tapped to turn
       edit mode back off. */
    @media (hover: hover) {
      #edit-mode-btn:hover { border-color: #4fd1c5; color: #4fd1c5; }
    }
    /* Highlight binds to the button's own .active class so the toggle
       state is decoupled from any deeper-cascade selectors that might
       beat the body-scoped rule on specificity. body.edit-mode also
       still applies for back-compat with older callers that read the
       body class for unrelated CSS. */
    body.edit-mode #edit-mode-btn,
    #edit-mode-btn.active {
      border-color: #4fd1c5; color: #4fd1c5;
      background: rgba(79, 209, 197, 0.12);
    }
    /* Suppress sticky tap-focus on the in-Sounds version so a click
       doesn't leave the highlight glued on via :focus on mobile. */
    .grid-settings-panel .sounds-utilities #edit-mode-btn:focus { outline: none; }
    /* Carrots visible only when global edit-mode is on. */
    body:not(.edit-mode) .cell-edit-carrot { display: none; }

    /* Grid / Graph per-lane toggle — lives at the right edge of the
       lane-expander's banner row, mirroring the radial-tone-btn slot
       on the left. Flat chrome (no pill radius) so it reads as part of
       the banner row rather than floating above it. Purple-tinted in
       Graph mode so the active fluid state is immediately legible. */
    #fluid-grid-toggle {
      flex: 0 0 auto;
      padding: 0 12px;
      background: transparent;
      border: none;
      /* Grid now sits at the LEFT edge of the banner row — drop the
         outer separator (border-right faces the rest of the row),
         keep the row-internal one. */
      border-right: 1px solid #1a1a2e;
      color: #a0aec0;
      font-family: 'Segoe UI', sans-serif;
      font-size: 0.7rem;
      font-weight: 700;
      letter-spacing: 1.5px;
      text-transform: uppercase;
      cursor: pointer;
      user-select: none;
      transition: background 0.15s ease, color 0.15s ease;
    }
    /* Master-volume speaker button — sits on the right edge of the
       banner row, mirroring #fluid-grid-toggle on the left. Width is
       synced to Grid in JS so the row stays balanced as the Grid
       label flips between "Grid" and "Graph". Clicking opens the
       master-vol-panel popover. */
    .banner-row-vol-btn {
      flex: 0 0 auto;
      padding: 0 12px;
      background: transparent;
      border: none;
      border-left: 1px solid #1a1a2e;
      color: #a0aec0;
      font-family: 'Segoe UI', sans-serif;
      font-size: 0.95rem;
      line-height: 1;
      cursor: pointer;
      user-select: none;
      transition: background 0.15s ease, color 0.15s ease;
      min-width: 48px;
    }
    @media (hover: hover) {
      .banner-row-vol-btn:hover { background: #0a0a14; color: #d6bcfa; }
    }
    .banner-row-vol-btn.open {
      background: rgba(159, 122, 234, 0.18);
      color: #ede9fe;
    }
    /* Master-volume popover. Opens absolutely positioned via pinPanel
       ToButton — same chrome as the other dropdowns. Slider fills the
       width so the user can drag with thumb or mouse without aiming. */
    .master-vol-panel {
      display: none;
      flex-direction: column;
      gap: 6px;
      position: absolute;
      top: calc(100% + 4px);
      right: 10px;
      background: #050510;
      border: 1px solid #2d2d3f;
      border-radius: 8px;
      padding: 14px 16px;
      z-index: 1200;
      min-width: 220px;
      box-shadow: 0 12px 36px rgba(0,0,0,0.55);
    }
    .master-vol-panel.open { display: flex; }
    .master-vol-panel .master-vol {
      display: flex;
      align-items: center;
      gap: 10px;
      width: 100%;
    }
    .master-vol-panel .master-vol-icon { font-size: 1rem; }
    .master-vol-panel input[type="range"] {
      flex: 1 1 auto;
      min-width: 0;
      width: 100%;
      accent-color: #9f7aea;
    }
    @media (hover: hover) {
      #fluid-grid-toggle:hover { background: #0a0a14; color: #d6bcfa; }
    }
    body.fluid-grid #fluid-grid-toggle {
      color: #ede9fe;
      background: rgba(159, 122, 234, 0.18);
    }
    /* Fluid mode visuals — drop the cell separators so the grid reads as
       a continuous surface. Per-cell carrots, freq labels, and the sound
       picker all hide so nothing breaks the unified field. Note labels
       stay so the user can still see where each pitch center sits. The
       cells.grid keeps its column-template intact so cell rects (used
       by the bilinear pitch interp) line up the same as Grid-On mode. */
    body.fluid-grid .grid { gap: 0; }
    body.fluid-grid .cell {
      border-width: 0;
      border-radius: 0;
      /* Cancel the cell's own pointer handling so the grid-level
         fluid handler owns the gesture and doesn't have to fight the
         per-cell pointerdown for the pointer. */
      touch-action: none;
    }
    body.fluid-grid .cell-edit-carrot,
    body.fluid-grid .cell-freq,
    body.fluid-grid .cell-tone,
    body.fluid-grid .cell-sound-select { display: none; }
    /* XY pad — shown only when fluid mode is on. Takes the same flex
       slot the cell grid had so the lane editor doesn't reflow. */
    #xy-pad {
      display: none;
      flex-direction: column;
      flex: 1;
      min-height: 0;
      gap: 6px;
    }
    body.fluid-grid #grid { display: none; }
    body.fluid-grid #xy-pad { display: flex; }
    /* XY pad axis controls — folded into the Sounds dropdown. Hidden
       in Grid mode so the panel stays clean; revealed in Graph mode
       via body.fluid-grid. Scale + Tone reuse the panel's existing
       controls — no duplicates.
       Selector matches BOTH the .grid-state-row and .xy-settings
       classes so it outranks the generic `.grid-state-row { display:
       flex }` rule that lives later in this file. Without that, the
       later display:flex won the cascade and the XY pad section kept
       appearing in Grid mode. */
    .grid-state-row.xy-settings { display: none; flex-direction: column; gap: 6px; }
    body.fluid-grid .grid-state-row.xy-settings { display: flex; }
    .xy-settings-label {
      color: #a0aec0; font-size: 0.78rem; font-weight: 600;
    }
    .xy-settings-row {
      display: flex; flex-wrap: wrap; gap: 6px; align-items: center;
      color: #cbd5e0; font-family: 'Segoe UI', sans-serif;
      font-size: 0.72rem;
    }
    .xy-settings-row label {
      display: inline-flex; align-items: center; gap: 4px;
    }
    .xy-settings-row select,
    .xy-settings-row input[type="number"] {
      background: #0a0a14; color: #e2e8f0;
      border: 1px solid #2d2d3f; border-radius: 4px;
      padding: 2px 6px; font-size: 0.75rem;
      font-family: 'Segoe UI', sans-serif;
      max-width: 90px;
    }
    .xy-settings-row input[type="number"] { width: 70px; }
    .xy-surface {
      flex: 1;
      /* Floor matches one octave's worth of cell-grid rows
         (3 rows × 70 px from .grid's grid-auto-rows). For taller
         octaveCount lanes JS bumps --grid-natural-h on #lane-expander
         so the XY surface tracks the grid's actual rendered height. */
      min-height: var(--grid-natural-h, 210px);
      position: relative;
      background: #050510;
      border: 1px solid #2d2d3f;
      border-radius: 6px;
      background-image:
        linear-gradient(to right, rgba(79, 209, 197, 0.06) 1px, transparent 1px),
        linear-gradient(to bottom, rgba(79, 209, 197, 0.06) 1px, transparent 1px);
      background-size: 25px 25px;
      touch-action: none;
      cursor: crosshair;
      overflow: hidden;
    }
    .xy-dot {
      position: absolute;
      width: 16px; height: 16px;
      border-radius: 50%;
      background: rgba(159, 122, 234, 0.85);
      box-shadow: 0 0 18px rgba(159, 122, 234, 0.85), 0 0 6px rgba(159, 122, 234, 0.55) inset;
      transform: translate(-50%, -50%);
      pointer-events: none;
      transition: opacity 0.12s ease;
    }
    /* Live X/Y readout — pinned to the bottom-right of the pad so it
       doesn't fight the dot for attention near the top-left corner the
       user usually starts a drag from. monospace for stable digit
       widths so values don't jitter horizontally as they change. */
    .xy-readout {
      position: absolute;
      right: 8px; bottom: 6px;
      font-family: 'SF Mono', 'Menlo', monospace;
      font-size: 0.72rem;
      color: #cbd5e0;
      background: rgba(5, 5, 16, 0.7);
      padding: 2px 8px;
      border-radius: 4px;
      pointer-events: none;
      white-space: nowrap;
      letter-spacing: 0.3px;
    }
    .xy-readout:empty { display: none; }
    /* Scale overlay — vertical / horizontal guide lines at every
       scale-note frequency within the active pitch axis's range, with
       a tiny note label at the edge of the surface. Pointer-events
       off so the dot underneath keeps capturing presses. */
    .xy-overlay {
      position: absolute; inset: 0;
      pointer-events: none;
      overflow: hidden;
    }
    .xy-grid-line { position: absolute; pointer-events: none; }
    .xy-grid-x { top: 0; bottom: 0; width: 1px; margin-left: -0.5px; opacity: 0.45; }
    .xy-grid-y { left: 0; right: 0; height: 1px; margin-top: -0.5px; opacity: 0.45; }
    .xy-grid-line .xy-grid-label {
      position: absolute;
      padding: 0 3px;
      background: rgba(5, 5, 16, 0.75);
      font-family: 'SF Mono', 'Menlo', monospace;
      font-size: 0.6rem;
      border-radius: 2px;
      letter-spacing: 0;
      pointer-events: none;
      white-space: nowrap;
    }
    .xy-grid-x .xy-grid-label { left: 0; bottom: 2px; transform: translateX(-50%); }
    .xy-grid-y .xy-grid-label { left: 2px; top: 0; transform: translateY(-50%); }
    /* Recording trail — one dot per captured XY sample. Sized to read
       as a faint continuous path when samples are dense; positioned by
       percentage so the trail scales with the surface on resize. */
    .xy-trail-dot {
      position: absolute;
      width: 4px; height: 4px;
      border-radius: 50%;
      background: rgba(159, 122, 234, 0.55);
      transform: translate(-50%, -50%);
      pointer-events: none;
      transition: transform 0.08s ease, filter 0.08s ease, box-shadow 0.08s ease;
    }
    /* Playback highlight — each dot scales up + glows white when its
       corresponding fluid-step sample is sounding. The .playing class
       is added by _playFluidStep via scheduleVisual at the right audio
       time and removed ~140 ms later. */
    .xy-trail-dot.playing {
      transform: translate(-50%, -50%) scale(2.4);
      filter: brightness(1.6);
      box-shadow: 0 0 14px 4px rgba(255, 255, 255, 0.6);
    }
    /* Master volume — a small inline slider in the menubar. The icon is
       a non-interactive label; the slider itself drives the mix gain. */
    .master-vol {
      display: inline-flex;
      align-items: center;
      gap: 6px;
      margin-left: 4px;
    }
    .master-vol-icon {
      font-size: 0.85rem;
      color: #4fd1c5;
      text-shadow: 0 0 8px rgba(79, 209, 197, 0.45);
      user-select: none;
    }
    .master-vol input[type="range"] {
      width: 90px;
      accent-color: #4fd1c5;
      cursor: pointer;
    }
    .project-name-label {
      display: inline-flex;
      align-items: center;
      max-width: 220px;
      overflow: hidden;
      text-overflow: ellipsis;
      white-space: nowrap;
      margin-left: -2px;
      padding: 2px 8px;
      color: #cbd5e0;
      font-family: 'Segoe UI', sans-serif;
      font-size: 0.78rem;
      font-weight: 600;
      letter-spacing: 0.3px;
    }
    .project-name-label:empty { display: none; }
    .project-panel {
      display: none;
      flex-direction: column;
      gap: 6px;
      position: absolute;
      top: calc(100% + 4px);
      left: 10px;
      background: #1a1a2e;
      border: 1px solid #2d2d3f;
      border-radius: 10px;
      padding: 10px;
      z-index: 9000;
      box-shadow: 0 8px 24px rgba(0,0,0,0.5);
      min-width: 180px;
    }
    .project-panel.open { display: flex; }
    .project-opt {
      padding: 6px 14px;
      border: 1px solid #2d2d3f;
      border-radius: 8px;
      background: transparent;
      color: #a0aec0;
      font-family: 'Segoe UI', sans-serif;
      font-size: 0.85rem;
      font-weight: 600;
      cursor: pointer;
      text-align: left;
      transition: all 0.15s ease;
    }
    .project-opt:hover { border-color: #4299e1; color: #4299e1; }
    .project-opt.project-opt-new:hover { border-color: #38a169; color: #68d391; }
    .project-opt:disabled { opacity: 0.5; cursor: not-allowed; }

    /* Riff dropdown — collapses Save / Random / Seed / Reverse / Shuffle /
       Repeat under one pill in the sequence-controls row. With Keep on,
       generated content is appended to the current sequence; with Keep
       off, it replaces (the original behavior). flex: 0 0 auto keeps the
       wrapper at content-width so it sits immediately adjacent to undo/
       redo instead of growing to push itself across the row. */
    .riff-wrapper { position: relative; display: inline-flex; flex: 0 0 auto; }
    #riff-menu-btn {
      flex: 0 0 auto;
      padding: 6px 14px;
      border: 1px solid #2d2d3f;
      border-radius: 20px;
      background: transparent;
      color: #a0aec0;
      font-family: 'Segoe UI', sans-serif;
      font-size: 0.8rem;
      font-weight: 600;
      cursor: pointer;
      transition: all 0.15s ease;
    }
    #riff-menu-btn:hover { border-color: #4299e1; color: #4299e1; }
    #riff-menu-btn.open  { border-color: #4299e1; color: #4299e1; background: rgba(66,153,225,0.12); }
    .riff-panel {
      display: none;
      flex-direction: column;
      gap: 6px;
      position: absolute;
      top: calc(100% + 4px);
      left: 0;
      background: #1a1a2e;
      border: 1px solid #2d2d3f;
      border-radius: 10px;
      padding: 10px;
      z-index: 9000;
      box-shadow: 0 8px 24px rgba(0,0,0,0.5);
      min-width: 160px;
    }
    .riff-panel.open { display: flex; }
    .riff-panel > button {
      flex: 0 0 auto;
      width: 100%;
      padding: 6px 14px;
      border: 1px solid #2d2d3f;
      border-radius: 8px;
      background: transparent;
      color: #a0aec0;
      font-family: 'Segoe UI', sans-serif;
      font-size: 0.85rem;
      font-weight: 600;
      cursor: pointer;
      text-align: left;
      transition: all 0.15s ease;
    }
    .riff-panel > button:hover:not(:disabled) { border-color: #4299e1; color: #4299e1; }
    .riff-panel > button:disabled { opacity: 0.4; cursor: not-allowed; }
    /* Random + Seed are pushed to the bottom of the Riff menu and
       tinted purple to flag that they replace the sequence wholesale,
       unlike Reverse / Shuffle / Repeat which just rearrange existing
       steps. Hover state matches the rest of the panel's color logic
       (border + text shift to the same accent). */
    .riff-panel > button.riff-generative {
      border-color: rgba(159, 122, 234, 0.55);
      color: #b794f4;
    }
    .riff-panel > button.riff-generative:hover:not(:disabled) {
      border-color: #9f7aea;
      color: #d6bcfa;
      background: rgba(159, 122, 234, 0.10);
    }
    .voices-panel {
      display: none;
      flex-direction: column;
      gap: 6px;
      position: absolute;
      top: calc(100% + 4px);
      left: 10px;
      background: #1a1a2e;
      border: 1px solid #2d2d3f;
      border-radius: 10px;
      padding: 10px;
      z-index: 9000;
      box-shadow: 0 8px 24px rgba(0,0,0,0.5);
      min-width: 200px;
      max-height: calc(100vh - 80px);
      overflow-y: auto;
    }
    .voices-panel.open { display: flex; }
    .voice-opt {
      padding: 6px 12px;
      border: 1px solid #2d2d3f;
      border-radius: 8px;
      background: transparent;
      color: #e2e8f0;
      font-family: 'Segoe UI', sans-serif;
      font-size: 0.85rem;
      font-weight: 600;
      cursor: pointer;
      text-align: left;
      display: flex;
      flex-direction: column;
      gap: 2px;
      transition: all 0.15s ease;
    }
    .voice-opt:hover { border-color: #4299e1; color: #4299e1; }
    .voice-opt.voice-opt-surprise:hover {
      border-color: #f472b6;
      color: #f9a8d4;
      background: rgba(244, 114, 182, 0.08);
    }
    .voice-opt-project {
      font-size: 0.7rem;
      font-weight: 500;
      color: #4a4a6a;
      letter-spacing: 0.5px;
    }
    .voice-loading, .voice-empty {
      color: #4a4a6a;
      font-family: 'Segoe UI', sans-serif;
      font-size: 0.8rem;
      padding: 6px 4px;
      text-align: center;
    }

    .grid-state-row {
      display: flex; flex-direction: column; align-items: stretch; gap: 6px;
      border-top: 1px solid #2d2d3f; padding-top: 10px;
    }
    .grid-state-row label { color: #a0aec0; font-size: 0.78rem; }
    #grid-state-select {
      width: 100%;
      padding: 4px 8px;
      background: #0a0a14;
      border: 1px solid #2d2d3f;
      border-radius: 6px;
      color: #e2e8f0;
      font-family: 'Segoe UI', sans-serif;
      font-size: 0.8rem;
    }
    .grid-state-actions { display: flex; gap: 6px; }
    .grid-state-actions button {
      flex: 1;
      padding: 5px 10px;
      background: transparent;
      border: 1px solid #2d2d3f;
      border-radius: 6px;
      color: #a0aec0;
      font-family: 'Segoe UI', sans-serif;
      font-size: 0.78rem;
      font-weight: 600;
      cursor: pointer;
      transition: all 0.15s ease;
    }
    .grid-state-actions button:hover { border-color: #4299e1; color: #4299e1; }
    .grid-state-actions #grid-state-delete-btn:hover { border-color: #e53e3e; color: #e53e3e; }
    .grid-state-actions button:disabled { opacity: 0.35; cursor: not-allowed; }
    /* Reset button gets a subtle red accent so it reads as a destructive
       action vs. the neutral Save/Delete/Load buttons above it. */
    .grid-state-actions #grid-reset-btn {
      border-color: #4a2c3a;
      color: #fda4a4;
    }
    @media (hover: hover) {
      .grid-state-actions #grid-reset-btn:hover {
        border-color: #e53e3e;
        color: #fc8181;
        background: rgba(229, 62, 62, 0.12);
      }
    }
    /* Tone… / FX… entries inside the Sounds panel. Teal gradient lifts
       them as the primary entry points into deeper sub-panels. */
    .sounds-submenus {
      display: flex; flex-direction: row; gap: 8px;
    }
    .sounds-submenus .sounds-sub-btn {
      flex: 1;
      padding: 8px 12px;
      background: linear-gradient(180deg, rgba(45, 212, 191, 0.10) 0%, rgba(45, 212, 191, 0.04) 100%);
      border: 1px solid rgba(45, 212, 191, 0.35);
      border-radius: 8px;
      color: #b9e9e1;
      font-family: 'Segoe UI', sans-serif;
      font-size: 0.84rem;
      font-weight: 700;
      letter-spacing: 0.6px;
      cursor: pointer;
      transition: border-color 0.15s ease, color 0.15s ease, background 0.15s ease, box-shadow 0.15s ease;
      text-align: center;
    }
    @media (hover: hover) {
      .sounds-submenus .sounds-sub-btn:hover {
        border-color: #4fd1c5;
        color: #d6f5f1;
        background: linear-gradient(180deg, rgba(45, 212, 191, 0.18) 0%, rgba(45, 212, 191, 0.08) 100%);
        box-shadow: 0 0 12px rgba(79, 209, 197, 0.20);
      }
    }
    /* Bottom row of the Sounds panel — Reset + Surprise-me dice
       centered together. The dice button is icon-only (label moved
       to title attr) so the row stays tight.
       `flex-direction: row` has to be explicit because the parent's
       .grid-state-row rule sets `flex-direction: column` and stacks
       its children otherwise. */
    .sounds-bottom-row {
      display: flex;
      flex-direction: row;
      align-items: center;
      justify-content: center;
      gap: 12px;
    }
    /* The Reset wrapper is a .grid-state-actions stub; flatten its
       flex so Reset itself sits centered next to the dice rather
       than stretching across the row. */
    .sounds-bottom-row .grid-state-actions {
      flex: 0 0 auto;
    }
    .sounds-dice-btn {
      flex: 0 0 auto;
      width: 36px;
      height: 36px;
      border-radius: 50%;
      background: linear-gradient(135deg, #6b46c1, #9f7aea);
      border: 1px solid rgba(159, 122, 234, 0.65);
      color: #fff;
      font-size: 1.05rem;
      line-height: 1;
      cursor: pointer;
      box-shadow:
        0 0 14px rgba(159, 122, 234, 0.45),
        0 4px 12px rgba(0, 0, 0, 0.45);
      transition: transform 0.15s ease, box-shadow 0.15s ease, filter 0.15s ease;
    }
    @media (hover: hover) {
      .sounds-dice-btn:hover {
        filter: brightness(1.15);
        box-shadow:
          0 0 22px rgba(159, 122, 234, 0.65),
          0 4px 16px rgba(0, 0, 0, 0.55);
        transform: scale(1.06) rotate(-8deg);
      }
    }
    .sounds-dice-btn:active {
      transform: scale(0.96) rotate(6deg);
    }
    /* Tuning row labels in the Sounds panel inherit the new color
       theme — pale text, accent hairline below for visual rhythm. */
    .grid-settings-panel .sounds-tuning-row label {
      color: #cbd5e0;
    }
    .grid-settings-panel .grid-control select,
    .grid-settings-panel .grid-control input[type=number] {
      background: #0c0c18;
      border-color: #3a3a55;
      color: #e2e8f0;
    }

    /* NOTE GRID */
    /* Slim clickable banners above the note grid that double as menu
       triggers — Scale + Tone share a row, FX sits below. Each half opens
       its corresponding settings panel and reflects the current value where
       applicable. Updated by updateScaleBanner() whenever the underlying
       state changes. */
    .banner-row {
      display: flex;
      flex-shrink: 0;
      background: #050510;
      border-bottom: 1px solid #1a1a2e;
    }
    .banner-row--fx { border-bottom: 1px solid #1a1a2e; }
    /* Radial Tone toggle — small icon-pill that sits to the left of the
       Scale banner. When on, the body gets .radial-tone and cells get a
       subtle radial-gradient overlay so the user can see the bend zones. */
    .radial-tone-btn {
      flex: 0 0 auto;
      padding: 0 10px;
      background: transparent;
      border: none;
      border-right: 1px solid #1a1a2e;
      color: #4a4a6a;
      font-family: 'Segoe UI', sans-serif;
      font-size: 1rem;
      font-weight: 700;
      cursor: pointer;
      letter-spacing: 0.5px;
      transition: color 0.15s ease, background 0.15s ease;
    }
    .radial-tone-btn:hover:not(:disabled) { color: #4fd1c5; }
    .radial-tone-btn:disabled {
      opacity: 0.35;
      cursor: not-allowed;
      color: #2d2d3f;
      text-shadow: none;
    }
    /* Active = filled purple pill (matches the cell bend-zone gradient) so
       the on/off contrast reads at a glance rather than blending into the
       teal banner row. */
    body.radial-tone .radial-tone-btn {
      color: #fff;
      background: linear-gradient(135deg, #6b46c1, #9f7aea);
      border-right-color: #9f7aea;
      text-shadow: 0 0 8px rgba(255, 255, 255, 0.85);
      box-shadow:
        0 0 14px rgba(159, 122, 234, 0.85),
        0 0 4px rgba(159, 122, 234, 0.6) inset;
      animation: radial-tone-pulse 1.4s ease-in-out infinite;
    }
    @keyframes radial-tone-pulse {
      0%, 100% { box-shadow: 0 0 14px rgba(159, 122, 234, 0.85),
                              0 0 4px  rgba(159, 122, 234, 0.6) inset; }
      50%      { box-shadow: 0 0 22px rgba(159, 122, 234, 1),
                              0 0 8px  rgba(159, 122, 234, 0.9) inset; }
    }
    /* Subtle bend-zone hint on every cell while Radial Tone is active —
       diagonal gradient from top-left (down) to bottom-right (up). Sits
       under the cell text so labels stay readable. */
    body.radial-tone .cell::before {
      content: '';
      position: absolute; inset: 0;
      pointer-events: none;
      border-radius: inherit;
      background: linear-gradient(135deg,
        rgba(155, 92, 229, 0.18) 0%,
        rgba(155, 92, 229, 0.04) 35%,
        rgba(0, 0, 0, 0) 50%,
        rgba(79, 209, 197, 0.04) 65%,
        rgba(79, 209, 197, 0.18) 100%);
      opacity: 0.9;
    }
    .banner-half {
      flex: 1;
      padding: 6px 12px;
      background: transparent;
      border: none;
      border-right: 1px solid #1a1a2e;
      color: #4fd1c5;
      font-family: 'Segoe UI', sans-serif;
      font-size: 0.7rem;
      font-weight: 700;
      letter-spacing: 1.5px;
      text-transform: uppercase;
      text-align: center;
      cursor: pointer;
      user-select: none;
      transition: background 0.15s ease, color 0.15s ease;
    }
    .banner-half:last-child { border-right: none; }
    .banner-half::after {
      content: ' ▾';
      font-size: 0.65rem;
      margin-left: 4px;
      color: #2d8478;
      transition: color 0.15s ease;
      display: inline-block;
    }
    @media (hover: hover) {
      .banner-half:hover { background: #0a0a14; color: #81e6d9; }
      .banner-half:hover::after { color: #81e6d9; }
    }
    .banner-half.open {
      background: #0a0a14;
      color: #81e6d9;
    }
    .banner-half.open::after {
      content: ' ▴';
      color: #81e6d9;
    }
    .grid {
      flex: 1;
      display: grid;
      grid-template-columns: repeat(4, 1fr);
      grid-auto-rows: minmax(70px, 1fr);
      min-height: 0;
      overflow-y: auto;
    }

    .cell {
      border: 3px solid #000;
      cursor: pointer;
      display: flex;
      align-items: center;
      justify-content: center;
      transition: filter 0.08s ease;
      user-select: none;
      -webkit-user-select: none;
      /* Suppress iOS Safari's tap-and-hold "select / copy / look up"
         callout — without it, holding a cell pops the native selection
         menu and pre-empts the JS long-press handler. */
      -webkit-touch-callout: none;
      position: relative;
      /* Tell the browser this element is for play gestures, not scroll —
         it commits to "play" without waiting for movement-direction
         heuristics, which used to leave touchmove + pointer events
         fighting and stalling page scroll on mobile. */
      touch-action: none;
    }

    .cell-sound-select {
      position: absolute;
      bottom: 5px;
      left: 5px;
      background: rgba(0,0,0,0.35);
      border: none;
      border-radius: 4px;
      color: rgba(255,255,255,0.8);
      font-size: 0.6rem;
      font-family: 'Segoe UI', sans-serif;
      font-weight: 600;
      padding: 2px 4px;
      cursor: pointer;
      appearance: none;
      -webkit-appearance: none;
      z-index: 1;
      outline: none;
    }
    .cell-sound-select:focus { background: rgba(0,0,0,0.6); }

    /* Tiny dropdown carrot in each cell's top-left — opens the sound editor.
       Replaces the previous double-tap gesture so the affordance is visible
       and cell taps don't have to compete with edit-intent. */
    .cell-edit-carrot {
      position: absolute;
      top: 4px; left: 4px;
      width: 18px; height: 18px;
      background: rgba(0, 0, 0, 0.45);
      color: rgba(255, 255, 255, 0.8);
      border: 1px solid rgba(255, 255, 255, 0.18);
      border-radius: 4px;
      font-size: 11px; line-height: 1;
      font-family: inherit;
      cursor: pointer;
      display: flex; align-items: center; justify-content: center;
      padding: 0; margin: 0;
      z-index: 2;
    }
    .cell-edit-carrot:hover { background: rgba(0, 0, 0, 0.7); color: #fff; }
    .cell.out-of-scale .cell-edit-carrot { visibility: hidden; }
    .cell:active        { filter: brightness(0.75); }
    .cell.flash         { filter: brightness(1.35); }
    .cell.active-loop   { filter: brightness(1.25); outline: 4px solid #fff inset; }
    /* Persistent press highlight — sticks for the full duration of the
       user's pointerdown→pointerup gesture (driven by polyStartCell /
       polyEndCellForPointer). Visible on top of .flash but yields to
       .active-loop's outline during sequencer playback so the playhead
       stays the dominant signal. */
    .cell.sustaining    { filter: brightness(1.45); }
    /* Persistent highlight for cells whose note is in the in-progress
       Wrap (chordMode). Cleared when Wrap is closed/committed. Tapping
       a wrap-pending cell again removes the note from the pending wrap. */
    .cell.wrap-pending  { filter: brightness(1.2); outline: 4px solid #9f7aea inset; }

    .cell span {
      font-family: 'Segoe UI', sans-serif;
      font-size: clamp(2.25rem, 6vw, 3.3rem);
      font-weight: 900;
      color: rgba(0,0,0,0.35);
      letter-spacing: 1px;
      pointer-events: none;
    }
    /* Drum-kit cells show the drum role ("Kick", "Open Hat", …) instead
       of the chromatic note name — these strings are longer than two
       characters so the body font has to come down to fit a 70-100px
       cell without overflow. */
    .cell span.cell-name-drum {
      font-size: clamp(0.85rem, 2.2vw, 1.15rem);
      letter-spacing: 0.4px;
      text-transform: uppercase;
      padding: 0 6px;
      text-align: center;
    }
    .cell .cell-freq {
      position: absolute;
      bottom: 4px;
      right: 6px;
      font-size: 0.62rem;
      font-weight: 600;
      letter-spacing: 0.4px;
      color: rgba(0,0,0,0.45);
    }
    /* Per-cell tone tag — only shown when the grid is in "Custom" state,
       i.e., at least one cell's tone has been changed away from the
       global setting. Top-left corner, small black text per spec. */
    .cell .cell-tone {
      position: absolute;
      top: 4px;
      left: 6px;
      font-size: 0.6rem;
      font-weight: 700;
      letter-spacing: 0.3px;
      color: rgba(0,0,0,0.78);
      pointer-events: none;
      max-width: calc(100% - 12px);
      overflow: hidden;
      text-overflow: ellipsis;
      white-space: nowrap;
      display: none;
    }
    .cell .cell-tone.show { display: inline-block; }

    .cell:nth-child(12n+1)  { background: #e63946; }
    .cell:nth-child(12n+2)  { background: #f4a261; }
    .cell:nth-child(12n+3)  { background: #e9c46a; }
    .cell:nth-child(12n+4)  { background: #2a9d8f; }
    .cell:nth-child(12n+5)  { background: #457b9d; }
    .cell:nth-child(12n+6)  { background: #9b5de5; }
    .cell:nth-child(12n+7)  { background: #f15bb5; }
    .cell:nth-child(12n+8)  { background: #00bbf9; }
    .cell:nth-child(12n+9)  { background: #06d6a0; }
    .cell:nth-child(12n+10) { background: #ffbe0b; }
    .cell:nth-child(12n+11) { background: #fb5607; }
    .cell:nth-child(12n)    { background: #8338ec; }

    /* REST BUTTON — small square sitting next to Step Mode in the BPM
       row. Earlier this was a full-width bar across the page; collapsed
       into a button so the BPM picker can stay centred on its own row. */
    :root { --rest-color: hsl(280, 55%, 78%); }
    .rest-bar {
      min-width: 92px;
      height: 52px;
      box-sizing: border-box;
      padding: 0 18px;
      background: #000;
      border: 2px solid var(--rest-color);
      border-radius: 10px;
      display: inline-flex;
      align-items: center;
      justify-content: center;
      cursor: pointer;
      user-select: none;
      transition: background 0.08s ease, border-color 0.12s ease, box-shadow 0.12s ease;
      box-shadow: 0 0 12px rgba(216, 180, 254, 0.18);
      /* Fill the right cluster so Rest stretches from the BPM picker
         toward the right side. The bar's 16px right padding stays
         untouched so there's a matching gutter between Rest's right
         edge and the screen edge. */
      flex: 1 1 auto;
      width: 100%;
      margin: -3px 0;
    }
    .rest-bar:hover      { border-color: #fff; box-shadow: 0 0 16px rgba(216, 180, 254, 0.32); }
    .rest-bar:active     { background: #1a1a2e; }
    .rest-bar.flash      { background: #2d2d3f; }
    .rest-bar.active-loop { background: #1a1a2e; outline: 2px solid var(--rest-color); outline-offset: 1px; }
    .rest-bar span {
      font-family: 'Segoe UI', sans-serif;
      font-size: 1rem;
      font-weight: 800;
      color: var(--rest-color);
      letter-spacing: 2.5px;
      text-transform: uppercase;
      text-shadow: 0 0 10px rgba(216, 180, 254, 0.55);
      pointer-events: none;
    }

    /* SEQUENCE DISPLAY */
    .sequence-display {
      min-height: 62px;
      background: #0a0a14;
      border-bottom: 3px solid #000;
      display: flex;
      flex-wrap: wrap;
      align-content: flex-start;
      align-items: center;
      padding: 8px 14px;
      /* row-gap is 16px to leave room for the absolute-positioned 12px-tall
         .row-interval markers that anchor at chip-bottom — without it they
         overflow into the next row's chips. column-gap stays tight at 6px. */
      row-gap: 16px;
      column-gap: 6px;
      /* Take only the natural content height (no max-height + no internal
         scroll, per CLAUDE.md: wrap, don't scroll). overflow: hidden keeps
         chips inside the display's box when the body flex layout squeezes
         it — without this, chips bleed visually into the Play/Rec/Loop bar
         below. Controls protect their own height via flex-shrink:0. */
      overflow: hidden;
      flex-shrink: 1;
      /* Anchor for the absolute-positioned interval markers (.row-interval)
         that label semitone gaps between consecutive chips on the same row. */
      position: relative;
    }
    /* Thin horizontal connector + label sitting between two adjacent step
       chips that share a row. Built post-render in renderIntervalBars()
       once chip layout is measurable. Currently hidden via display:none
       — the JS still computes positions but nothing renders. Drop the
       display rule to bring the markers back. */
    .row-interval {
      display: none;
      position: absolute;
      height: 12px;
      pointer-events: none;
      user-select: none;
      font-family: 'Segoe UI', sans-serif;
      font-size: 9px;
      font-weight: 700;
      color: #6e7596;
      letter-spacing: 0.5px;
      align-items: center;
      justify-content: center;
      z-index: 1;
    }
    .row-interval::before {
      content: '';
      position: absolute;
      left: 0; right: 0;
      top: 50%;
      height: 1px;
      background: #2d2d3f;
    }
    .row-interval > span {
      position: relative;
      background: #0a0a14;
      padding: 0 4px;
    }
    /* Grid layout: fixed N columns. Chips drop their explicit pixel width
       and stretch to fill their grid cell. Triggered by JS setting both the
       class and the --grid-cols custom property. */
    .sequence-display.grid-mode {
      display: grid;
      grid-template-columns: repeat(var(--grid-cols, 16), 1fr);
      align-items: stretch;
      max-height: none;
      /* Tight rows now that interval markers are hidden — restore the
         16px row-gap if/when .row-interval is unhidden. */
      row-gap: 2px;
      column-gap: 0;
    }

    /* Poly-mode lanes: one row per lane, each with a small label/M/S
       column on the left and a side-scrolling chip strip on the right.
       Only the active lane gets full editing handlers; other lanes
       render preview chips and click anywhere to activate. */
    .sequence-display.polymode {
      display: flex;
      flex-direction: column;
      flex-wrap: nowrap;
      align-items: stretch;
      gap: 4px;
      padding: 6px 0 6px 0;
      /* No internal scroll — per CLAUDE.md the display grows to fit
         every step, and the page scrolls when content exceeds the
         viewport. Chips inside .lane-chips already wrap via
         flex-wrap: wrap / grid auto-flow, so multi-row lanes get
         taller rows instead of a hidden overflow. */
      overflow-y: visible;
    }
    .sequence-display.polymode.grid-mode {
      /* Override the grid layout so polymode's flex-column wins. */
      display: flex;
    }
    .lane-row {
      display: flex;
      align-items: stretch;
      gap: 6px;
      padding: 2px 6px;
      border-radius: 8px;
      cursor: pointer;
      min-height: 44px;
      transition: background 0.12s ease;
    }
    /* Lane voice editor — the cell grid + Scale/Tone/FX/Spell pills.
       renderSequence reparents this node from #lane-expander-stash to
       sit just before the active .lane-row. The close button (top
       right) and Esc both collapse it back into the stash. */
    #lane-expander {
      position: relative;
      display: flex;
      flex-direction: column;
      gap: 4px;
      margin: 4px 0 6px;
      padding: 6px 8px 8px;
      border: 1px solid rgba(66, 153, 225, 0.35);
      border-radius: 10px;
      background: rgba(20, 22, 36, 0.55);
      box-shadow: inset 0 0 0 1px rgba(66, 153, 225, 0.10);
    }
    #lane-expander > .grid {
      /* The grid keeps its own internal layout — this just trims
         the default outer margins so it lines up inside the panel. */
      margin: 0;
    }
    .lane-expander-close {
      position: absolute;
      top: 4px;
      right: 6px;
      width: 22px;
      height: 22px;
      padding: 0;
      border: 1px solid rgba(255, 255, 255, 0.18);
      border-radius: 50%;
      background: rgba(0, 0, 0, 0.35);
      color: #cbd5e0;
      font-size: 14px;
      line-height: 1;
      cursor: pointer;
      z-index: 2;
    }
    .lane-expander-close:hover {
      color: #fff;
      border-color: rgba(255, 255, 255, 0.45);
      background: rgba(0, 0, 0, 0.55);
    }
    /* The stash itself is hidden via [hidden]. We also belt-and-
       brace with display:none so any rule that relies on layout
       can't accidentally surface the parked element. */
    #lane-expander-stash[hidden] { display: none; }
    .lane-row.active {
      background: rgba(66, 153, 225, 0.10);
      box-shadow: inset 0 0 0 1px rgba(66, 153, 225, 0.45);
      cursor: default;
    }
    .lane-row.muted .lane-chips,
    .lane-row.muted .lane-label { opacity: 0.35; }
    .lane-row.soloed { box-shadow: inset 0 0 0 1px rgba(246, 173, 85, 0.5); }
    .lane-row.active.soloed { box-shadow: inset 0 0 0 1px #f6ad55; }
    /* Single status-light button per lane — replaces the old separate
       label + Mute + Solo. The button's background colour reflects
       the lane's mute/solo state; the lane label is centred on top.
       Stretches to fill the controls column for max click area. */
    .lane-controls {
      display: flex;
      flex-direction: row;
      flex: 0 0 auto;
      align-items: stretch;
      justify-content: stretch;
      gap: 4px;
      padding: 2px;
      /* Collapse + status pill — the grid-toggle chevron was removed
         from the controls cluster, leaving just collapse and status.
         Width trimmed ~30% from the previous 92 px to free room for
         the chip strip on narrow viewports. */
      width: 64px;
      min-width: 64px;
      /* Pin the controls to a single-row height and vertically center
         them inside the lane-row, so when the lane's chip area wraps
         into multiple rows (taller lane-row) the leftmost buttons stay
         the same size instead of stretching with the row. */
      height: 40px;
      align-self: center;
    }
    .lane-collapse-toggle,
    .lane-grid-toggle {
      flex: 0 0 22px;
      width: 22px;
      border: 2px solid #2d2d3f;
      border-radius: 8px;
      background: rgba(45, 45, 63, 0.4);
      color: #cbd5e0;
      font-family: 'Segoe UI', sans-serif;
      font-size: 0.95rem;
      line-height: 1;
      cursor: pointer;
      transition: all 0.15s ease;
      touch-action: manipulation;
      padding: 0;
    }
    .lane-collapse-toggle:hover,
    .lane-grid-toggle:hover {
      border-color: #4299e1;
      color: #b3d4ff;
    }
    .lane-collapse-toggle.open,
    .lane-grid-toggle.open {
      border-color: #4299e1;
      color: #b3d4ff;
      background: rgba(66, 153, 225, 0.18);
    }
    .lane-status {
      flex: 1 1 auto;
      width: 100%;
      border: 2px solid #2d2d3f;
      border-radius: 8px;
      background: rgba(45, 45, 63, 0.4);
      color: #cbd5e0;
      font-family: 'Segoe UI', sans-serif;
      font-size: 0.85rem;
      font-weight: 700;
      letter-spacing: 0.4px;
      cursor: pointer;
      transition: all 0.15s ease;
      touch-action: manipulation;
      padding: 0;
    }
    .lane-row.active .lane-status { color: #b3d4ff; border-color: #4299e1; }
    .lane-status[data-state="muted"] {
      background: rgba(229, 62, 62, 0.18);
      border-color: #e53e3e;
      color: #fc8181;
    }
    .lane-status[data-state="solo"] {
      background: rgba(246, 173, 85, 0.20);
      border-color: #f6ad55;
      color: #fbd38d;
    }
    .lane-row.active .lane-status[data-state="muted"] {
      box-shadow: 0 0 10px rgba(229, 62, 62, 0.45);
    }
    .lane-row.active .lane-status[data-state="solo"] {
      box-shadow: 0 0 10px rgba(246, 173, 85, 0.5);
    }
    /* Non-active lanes: status button is read-only — show the colour
       state but flag it as not directly interactive so the user knows
       to switch lanes first. */
    .lane-status.inactive {
      cursor: default;
      opacity: 0.85;
    }
    @media (hover: hover) {
      .lane-row.active .lane-status:hover { filter: brightness(1.15); }
    }
    @media (hover: hover) {
      .lane-btn:hover { border-color: #4299e1; color: #4299e1; }
    }
    .lane-mute.on { border-color: #e53e3e; color: #e53e3e; background: rgba(229, 62, 62, 0.15); }
    .lane-solo.on { border-color: #f6ad55; color: #f6ad55; background: rgba(246, 173, 85, 0.18); }
    .lane-chips {
      flex: 1 1 auto;
      min-width: 0;
      display: flex;
      flex-direction: row;
      /* Default: wrap chips so every step is visible without
         horizontal scrolling. Lane row grows vertically to fit. */
      flex-wrap: wrap;
      align-items: stretch;
      gap: 4px;
      overflow-x: hidden;
      overflow-y: visible;
      padding: 4px 6px;
      background: rgba(10, 10, 20, 0.55);
      border-radius: 6px;
      scrollbar-width: thin;
      scrollbar-color: #2d2d3f #0a0a14;
    }
    /* Collapsed lane: hide the chip strip entirely so just the lane
       controls (status pill + collapse + grid toggles) remain visible.
       Driven by the collapse button on lane-controls. */
    .lane-row.collapsed .lane-chips {
      display: none;
    }
    /* Consecutive collapsed lanes pack horizontally into a single
       strip (renderSequence groups them) so 3 minimized lanes read
       as one slim row instead of three full-width ones. */
    .lane-row-collapsed-strip {
      display: flex;
      flex-wrap: wrap;
      gap: 4px;
      padding: 0 6px;
    }
    .lane-row-collapsed-strip > .lane-row.collapsed {
      flex: 0 0 auto;
      min-height: 0;
      padding: 2px;
    }
    /* Inside the strip the controls cluster shrinks since there's
       no chip strip to balance against. */
    .lane-row-collapsed-strip > .lane-row.collapsed .lane-controls {
      width: auto;
      min-width: auto;
    }
    .lane-chips::-webkit-scrollbar { height: 4px; }
    .lane-chips::-webkit-scrollbar-track { background: #0a0a14; }
    .lane-chips::-webkit-scrollbar-thumb { background: #2d2d3f; border-radius: 2px; }
    .lane-chips.grid-mode {
      /* Each lane chip strip uses the same sub-cell grid as Mono. The
         inline grid overrides flex so chips lay out by gridColumn span.
         `row` (not `row dense`) preserves musical order: a chip that
         can't fit on the current row wraps to the next, instead of
         letting a later short chip backfill the gap and reorder the
         sequence visually. The gaps are the price of in-order display.
         `minmax(0, 1fr)` lets cells shrink below any pixel floor so
         high grid-cols values never push the row off-screen on a
         narrow phone. The previous 8 px minimum × 32 sub-cells × 4 px
         gaps came out to ~380 px, which overflowed sub-400 px viewports
         and clipped the right side of the lane. */
      display: grid;
      grid-template-columns: repeat(var(--grid-cols, 32), minmax(0, 1fr));
      grid-auto-flow: row;
      align-items: stretch;
      /* Zero gap so chip boundaries land exactly on grid-cell
         boundaries — any positive gap would shift each successive
         cell rightward by `(gap × cell-index)` and the time-grid
         background (which uses straight content-width / grid-cols
         math) would no longer align with the chip edges. Chip
         borders give all the visual separation we need. */
      gap: 0;
      /* Time-grid background — vertical lines every 1 sub-cell (minor)
         plus a stronger line every 4 sub-cells (1/8 note major). The
         pattern stretches with --grid-cols so changing the rows count
         re-aligns the lines with the actual chip boundaries. Lines
         sit behind the chips so they read as a subtle ruler.
         background-origin: content-box + background-clip: content-box
         shifts the painting area inward by the .lane-chips padding so
         the lines start at the same x where chip cell 1 starts.
         Without it, the 100%-width tile included the padding and
         every cell drifted ~0.4 px / cell to the right, accumulating
         a visible offset across a long row. */
      background-image:
        linear-gradient(to right, rgba(160,174,192,0.08) 1px, transparent 1px),
        linear-gradient(to right, rgba(160,174,192,0.20) 1px, transparent 1px);
      background-size:
        calc(100% / var(--grid-cols, 32)) 100%,
        calc(400% / var(--grid-cols, 32)) 100%;
      background-position: 0 0;
      background-repeat: repeat;
      background-origin: content-box;
      background-clip:   content-box;
    }
    .lane-chips .seq-empty {
      color: #4a4a6a;
      font-family: 'Segoe UI', sans-serif;
      font-size: 0.78rem;
      align-self: center;
      padding: 0 6px;
    }

    /* Mono / Poly toggle pill — same chrome as Free/Fixed for visual
       parity. Active state lights up cyan when Poly is on. */
    #poly-mode-btn {
      padding: 3px 14px;
      border: 1px solid #2d2d3f;
      border-radius: 14px;
      background: transparent;
      color: #a0aec0;
      font-family: 'Segoe UI', sans-serif;
      font-size: 0.85rem;
      font-weight: 700;
      letter-spacing: 0.5px;
      cursor: pointer;
      transition: all 0.15s ease;
      touch-action: manipulation;
    }
    @media (hover: hover) {
      #poly-mode-btn:hover { border-color: #4299e1; color: #4299e1; }
    }
    #poly-mode-btn.active {
      border-color: #4299e1;
      color: #b3d4ff;
      background: rgba(66, 153, 225, 0.18);
      box-shadow: 0 0 12px rgba(66, 153, 225, 0.4);
    }
    /* Mobile: drop the chip's text label. Narrow chips on small
       viewports couldn't fit a useful chunk of "C4·E4·G4 ¼" anyway,
       and the truncated leading character read as noise more than
       information. Pseudo-element indicators (bend arrows, variance,
       active ring) use rem-based font sizes so they survive font-size:0. */
    @media (max-width: 700px) {
      .lane-chips .seq-step,
      .sequence-display .seq-step {
        font-size: 0;
      }
    }
    .sequence-display.grid-mode .seq-step,
    .lane-chips.grid-mode .seq-step {
      width: auto;
      /* Grid items default to min-width: auto, which resolves to the
         content's min-content size — for a chord chip's "C4·E4·G4"
         that's the full unbreakable string, so the chip ignored its
         assigned grid-column span and pushed siblings off. Forcing
         min-width: 0 lets the chip honor its track size; overflow:
         hidden (inherited from .seq-step) clips the chord label so
         the visible text truncates to whatever fits the step-div
         width. */
      min-width: 0;
      min-height: 0;
      /* Truncate, don't wrap. Earlier this lane allowed multi-line
         stacking via overflow-wrap: anywhere on narrow chips, but
         small chord chips on mobile then grew vertically (or the
         text broke mid-pitch) and the chip stopped matching its
         assigned grid-track size. Force a single line and let
         overflow: hidden (inherited from .seq-step base) clip the
         label to the chip's actual width. */
      white-space: nowrap;
      text-overflow: clip;
    }
    /* Continuation segments — a step that's too long to fit on one
       row renders as a leading head chip (.cont-start) plus one or
       more decorative segments (.cont-mid / .cont-end). Sharp inside
       edges + dashed seam where the step crosses a row break make the
       linkage between segments readable without burning a chip per
       sub-cell. The segments inherit the head chip's color via the
       cloneNode in renderSequence. */
    .seq-step.cont-start { border-top-right-radius: 0; border-bottom-right-radius: 0; border-right-style: dashed; }
    .seq-step.cont-mid {
      border-radius: 0;
      border-left-style: dashed;
      border-right-style: dashed;
      pointer-events: none;
    }
    .seq-step.cont-end {
      border-top-left-radius: 0;
      border-bottom-left-radius: 0;
      border-left-style: dashed;
      pointer-events: none;
    }
    /* All continuation segments are visual only — no hover lift, no
       click target. Reduce their text presence (already blanked in
       JS, but in case any cloneNode debris slips through) and dim
       slightly so the head chip reads as the "real" target. */
    .seq-step.cont-segment {
      cursor: default;
      opacity: 0.92;
    }
    /* Empty pattern slot used while step-mode is on. */
    .seq-step.empty-slot {
      background: rgba(45, 45, 63, 0.35);
      border: 1px dashed #2d2d3f;
      color: #4a4a6a;
      cursor: pointer;
    }
    .seq-step.empty-slot:hover { border-color: #4299e1; color: #4299e1; }
    /* Highlight the currently-armed grid cell while step-mode is on. */
    .cell.step-mode-armed {
      outline: 2px solid #f6ad55;
      outline-offset: -2px;
      box-shadow: 0 0 12px rgba(246, 173, 85, 0.6);
    }

    .seq-empty {
      color: #2d2d3f;
      font-family: 'Segoe UI', sans-serif;
      font-size: 0.85rem;
      white-space: nowrap;
      pointer-events: none;
    }

    .seq-step {
      width: 80px;
      height: 38px;
      border-radius: 8px;
      background: #1a1a2e;
      border: 1px solid #2d2d3f;
      display: flex;
      align-items: center;
      justify-content: center;
      font-family: 'Segoe UI', sans-serif;
      font-size: 0.75rem;
      font-weight: 700;
      color: #a0aec0;
      user-select: none;
      -webkit-user-select: none;
      -webkit-touch-callout: none;
      cursor: grab;
      position: relative;
      flex-shrink: 0;
      overflow: hidden;
      white-space: pre-line;
      text-align: center;
      transition: all 0.1s ease;
      /* Lets vertical page-scroll pass through chips on mobile while
         horizontal gestures (pan-on-hold, reorder drag) stay capturable
         by JS pointer handlers. Without this the page scroll would
         get eaten whenever the user's thumb landed on a chip. */
      touch-action: pan-y;
    }
    .seq-step.rest { color: #4a4a6a; }
    /* Per-step pan glow — blue for right pan, purple for left. Uses
       layered box-shadows so the chip's own border-color (set inline by
       the palette tint) keeps showing through, and the .active /
       .selected box-shadows still override when the chip is playing or
       being edited. */
    /* Fixed-mode slot builder. Each chip appends a programmable rest
       slot of the given Step Div size to the sequence; visible only
       when the Free/Fixed toggle is in Fixed mode. Sits between the
       KEEP/Wrap/REST row and the sequence chip display. */
    .size-chips-row {
      display: flex;
      gap: 6px;
      padding: 4px 18px;
      background: #050510;
      border-bottom: 1px solid #1a1a2e;
      flex-wrap: nowrap;
      overflow-x: auto;
    }
    .size-chips-row[hidden] { display: none; }
    .size-chip {
      flex: 1 1 0;
      min-width: 0;
      padding: 6px 4px;
      border: 1px solid #2d2d3f;
      border-radius: 20px;
      background: transparent;
      color: #a0aec0;
      font-family: 'Segoe UI', sans-serif;
      /* Scale the label down on narrow viewports so the longest chip
         ("1/32") never spills past its cell. clamp keeps a readable
         floor and prevents the font from growing past 0.78rem. */
      font-size: clamp(0.55rem, 1.6vw, 0.78rem);
      font-weight: 600;
      cursor: pointer;
      transition: all 0.15s ease;
      white-space: nowrap;
      overflow: hidden;
      text-overflow: ellipsis;
      text-align: center;
    }
    .size-chip:hover { border-color: #4299e1; color: #4299e1; }
    .size-chip:active { background: rgba(66,153,225,0.18); }

    /* Step Edit row: holds an "Edit" button on the left (10vw wide,
       full-row height = both sliders) and the Pan / Vol sliders
       stacked on the right. The wrapper hides when neither slider
       bar is being shown. */
    .step-edit-row {
      display: flex;
      align-items: stretch;
      background: #050510;
      border-bottom: 1px solid #1a1a2e;
    }
    .step-edit-row[hidden] { display: none; }
    .step-edit-row > #step-edit-btn {
      flex: 0 0 10vw;
      width: 10vw;
      max-width: 10vw;
      margin: 4px 6px 4px 18px;
      padding: 6px 4px;
      border: 1px solid #2d2d3f;
      border-radius: 10px;
      background: transparent;
      color: #a0aec0;
      font-family: 'Segoe UI', sans-serif;
      font-size: 0.78rem;
      font-weight: 700;
      letter-spacing: 0.6px;
      text-transform: uppercase;
      cursor: pointer;
      transition: all 0.15s ease;
      touch-action: manipulation;
    }
    .step-edit-row > #step-edit-btn[hidden] { display: none; }
    @media (hover: hover) {
      .step-edit-row > #step-edit-btn:hover { border-color: #4299e1; color: #4299e1; }
    }
    .step-edit-row > #step-edit-btn:active { background: rgba(66,153,225,0.18); }
    .step-edit-row > .step-edit-bars {
      flex: 1 1 auto;
      min-width: 0;
      display: flex;
      flex-direction: column;
    }
    /* Selected-step pan slider — appears just below the BPM row when
       at least one step chip is selected, and applies its value to the
       whole selection (multi-mode included). The chip background still
       lerps toward the L/R hard-pan colour so the visual cue tracks
       the slider live. */
    .step-pan-bar, .step-vol-bar, .step-slip-bar {
      display: flex;
      align-items: center;
      gap: 12px;
      padding: 4px 18px;
      background: #050510;
    }
    .step-pan-bar, .step-vol-bar { border-bottom: 1px solid #1a1a2e; }
    .step-pan-bar[hidden], .step-vol-bar[hidden], .step-slip-bar[hidden] { display: none; }
    .step-pan-label, .step-vol-label, .step-slip-label {
      font-family: 'Segoe UI', sans-serif;
      font-size: 0.7rem;
      font-weight: 700;
      letter-spacing: 1.5px;
      text-transform: uppercase;
      color: #a0aec0;
    }
    .step-pan-bar input[type="range"],
    .step-vol-bar input[type="range"],
    .step-slip-bar input[type="range"] {
      flex: 1;
      height: 4px;
      accent-color: #4fd1c5;
    }
    .step-pan-val, .step-vol-val, .step-slip-val {
      min-width: 36px;
      text-align: right;
      font-family: 'Segoe UI', sans-serif;
      font-size: 0.78rem;
      font-weight: 700;
      color: #b2f5ea;
    }

    .seq-cursor {
      width: 4px;
      height: 38px;
      background: #68d391;
      border-radius: 2px;
      cursor: pointer;
      flex-shrink: 0;
      box-shadow: 0 0 8px rgba(104, 211, 145, 0.7);
      animation: seqCursorPulse 1.1s ease-in-out infinite;
    }
    @keyframes seqCursorPulse {
      0%, 100% { opacity: 1; }
      50% { opacity: 0.45; }
    }
    .seq-step.subseq {
      border-color: #68d391;
      color: #68d391;
      background: rgba(104, 211, 145, 0.08);
    }
    .seq-step.active {
      background: linear-gradient(135deg, #d91e7c, #f472b6);
      border-color: #f472b6;
      color: #fff;
      box-shadow: 0 0 14px rgba(244, 114, 182, 0.55);
    }
    /* Click-selected step — distinguishes "the step I'm editing" from "the
       step currently playing". Cyan double-outline pairs with the play
       button color but doesn't fight the magenta active highlight. */
    .seq-step.selected:not(.active) {
      outline: 2px solid #00bfff;
      outline-offset: -3px;
      box-shadow: 0 0 12px rgba(0, 191, 255, 0.45);
    }
    .seq-step.subseq.active {
      background: linear-gradient(135deg, #38a169, #68d391);
      border-color: #68d391;
      color: #fff;
    }

    #subedit-banner {
      display: none;
      align-items: center;
      gap: 12px;
      margin: 6px 8px 0;
      padding: 6px 12px;
      background: rgba(104, 211, 145, 0.12);
      border: 1px solid #68d391;
      border-radius: 8px;
      color: #68d391;
      font-family: 'Segoe UI', sans-serif;
      font-size: 0.82rem;
      font-weight: 600;
    }
    #subedit-banner.open { display: flex; }
    #subedit-cancel {
      margin-left: auto;
      padding: 4px 12px;
      border: 1px solid #68d391;
      background: transparent;
      color: #68d391;
      border-radius: 14px;
      font-family: inherit;
      font-size: 0.78rem;
      font-weight: 600;
      cursor: pointer;
      transition: all 0.15s ease;
    }
    #subedit-cancel:hover { background: rgba(104, 211, 145, 0.15); }

    /* FOOTER */
    .footer-bar {
      min-height: 48px;
      background: #000;
      display: flex;
      flex-wrap: wrap;
      align-items: center;
      padding: 6px 20px;
      gap: 10px;
    }
    /* Row that hosts the moved Step controls (Wrap / Size / Multi) —
       sits between the BPM bar and the sequence display so step
       creation defaults are right where the user is reading. */
    /* .step-controls-row was retired when Size + Multi moved into the
       seq-grid-bar alongside Grid + Free. Selectors kept commented out
       in case the row is reintroduced.
    .step-controls-row { ... }
    .step-controls-row .btn-group--step { ... }
    */

    /* When the Step cluster lives inside seq-grid-bar (alongside Grid +
       Free), it should sit as a compact inline pillbox rather than the
       full-width row it used to be. Override the standalone width and
       padding so it tucks into the row without breaking the layout. */
    .seq-grid-bar .btn-group--step {
      flex: 0 0 auto;
      width: auto;
      padding: 4px 10px;
      gap: 6px;
    }
    .seq-grid-bar .btn-group--step > *:not(.btn-group-label) {
      flex: 0 0 auto;
    }

    .btn-group {
      display: flex;
      flex-wrap: wrap;
      align-items: center;
      gap: 8px;
    }
    /* Stretch buttons (and select-bearing labels) to share row width. The
       group label stays auto-sized so it doesn't grow with the rest. */
    .btn-group--seq > button {
      flex: 1 1 80px;
    }
    .btn-group--step > *:not(.btn-group-label) {
      flex: 1 1 80px;
    }
    .btn-group--step .note-length-label {
      justify-content: space-between;
    }
    .btn-group--step .note-length-label select {
      flex: 1 1 auto;
    }
    .btn-group + .btn-group {
      padding-left: 12px;
      margin-left: 2px;
      border-left: 1px solid #2d2d3f;
    }
    @media (max-width: 700px) {
      .btn-group + .btn-group {
        padding-left: 0;
        margin-left: 0;
        border-left: none;
      }
    }

    /* Step controls cluster — its own row above the play button, framed
       with a thin teal border so it reads as a coherent group apart from
       the transport / sequence controls. */
    .btn-group--step {
      flex-basis: 100%;
      padding: 6px 12px;
      border: 1px solid rgba(79, 209, 197, 0.45);
      border-radius: 12px;
      background: rgba(79, 209, 197, 0.05);
    }
    .btn-group--step + .btn-group {
      border-left: none;
      padding-left: 0;
      margin-left: 0;
    }
    .btn-group-label {
      font-family: 'Segoe UI', sans-serif;
      font-size: 0.7rem;
      font-weight: 700;
      letter-spacing: 1px;
      text-transform: uppercase;
      color: #4fd1c5;
      padding-right: 4px;
    }
    .btn-group--step #chord-btn {
      border-color: #4fd1c5;
      color: #81e6d9;
    }
    .btn-group--step #chord-btn:hover {
      border-color: #81e6d9;
      color: #b2f5ea;
    }
    .btn-group--step #chord-btn.active {
      border-color: #9f7aea;
      background: linear-gradient(135deg, #553c9a, #9f7aea);
      color: #fff;
      box-shadow: 0 0 14px rgba(159, 122, 234, 0.55);
    }
    .btn-group--step.chord-active {
      border-color: #9f7aea;
      background: rgba(159, 122, 234, 0.10);
    }
    .btn-group--step.chord-active .btn-group-label {
      color: #d6bcfa;
    }
    /* Highlight the Step section while a chip is selected — clicking a grid
       cell will retune the selected step rather than appending a new one,
       so the pink outline signals "you're editing, not creating". */
    .btn-group--step.step-selected {
      border-color: #ed64a6;
      background: rgba(237, 100, 166, 0.10);
    }
    .btn-group--step.step-selected .btn-group-label { color: #fbb6ce; }
    .btn-group--step .note-length-label { color: #81e6d9; }
    .btn-group--step #note-length,
    .btn-group--step #subdivision-select {
      border-color: #4fd1c5;
      color: #b2f5ea;
      background: #1a1a2e;
      border-radius: 6px;
      padding: 3px 6px;
      font-size: 0.8rem;
      font-family: 'Segoe UI', sans-serif;
    }
    .btn-group--step #note-length:focus,
    .btn-group--step #subdivision-select:focus {
      border-color: #81e6d9;
    }

    /* Standalone Grid + Step Mode bar — sits directly under the REST bar so
       the layout / step-sequencer toggles aren't buried inside the step-edit
       cluster at the bottom. */
    .step-grid-bar {
      background: #050510;
      border-bottom: 3px solid #000;
      /* Single full-width pill (Keep / Wrap / Rest). The 16px horizontal
         padding preserves the left/right gutters Keep and Rest had as
         standalone clusters. */
      display: block;
      padding: 6px 16px;
    }
    .step-grid-bar-left {
      /* Stretch across the 1fr column so the Keep button can expand to
         fill all the room between the screen's left edge and the
         centred BPM picker. */
      justify-self: stretch;
      display: flex;
      align-items: center;
      gap: 8px;
      min-width: 0;
      max-width: 100%;
    }
    /* No standalone chord-display element — when a wrap is active the
       Keep button itself flips its label to the chord name (and adds
       .show-chord). See updateKeepLabel() and the .play-lock.show-chord
       overrides below. */
    .step-grid-bar-right {
      /* Mirror the left cluster — stretch the column so Rest can fill
         the space from the BPM's right edge to the screen's right edge. */
      justify-self: stretch;
      display: flex;
      align-items: center;
      justify-content: flex-end;
      gap: 8px;
      min-width: 0;
      max-width: 100%;
    }
    /* Left-side Rest mirrors the right one — pull into the bar's
       padding on top, left, and bottom (right edge unchanged so the
       gap to the centred BPM picker stays the same). */
    .rest-bar--left { margin: -3px 0 -3px -8px; }
    /* Lock button — sits in the slot the left Rest button used to
       occupy. Same physical footprint as the right Rest so the BPM
       picker stays visually centred between them. */
    .play-lock {
      min-width: 92px;
      height: 52px;
      box-sizing: border-box;
      padding: 0 18px;
      background: #000;
      border: 2px solid #f6ad55;
      border-radius: 10px;
      display: inline-flex;
      align-items: center;
      justify-content: center;
      cursor: pointer;
      user-select: none;
      transition: background 0.08s ease, border-color 0.12s ease, box-shadow 0.12s ease;
      box-shadow: 0 0 12px rgba(246, 173, 85, 0.18);
      /* Fill the left cluster so Keep stretches toward the centred BPM
         picker. The bar's 16px left padding stays untouched so there's
         a comfortable gutter between Keep's left edge and the screen
         edge. */
      flex: 1 1 auto;
      width: 100%;
      margin: -3px 0;
    }
    .play-lock:hover { border-color: #fbd38d; box-shadow: 0 0 16px rgba(246, 173, 85, 0.32); }
    /* Active state shifts to a hot-pink palette so the on/off contrast
       reads at a glance, plus a steady glow pulse to scream "armed". */
    .play-lock.active {
      background: rgba(244, 114, 182, 0.22);
      border-color: #f9a8d4;
      box-shadow:
        0 0 22px rgba(244, 114, 182, 0.85),
        0 0 8px rgba(244, 114, 182, 0.55) inset;
      animation: play-lock-pulse 0.95s ease-in-out infinite;
    }
    .play-lock.active span {
      color: #fbcfe8;
      text-shadow: 0 0 14px rgba(249, 168, 212, 0.95);
    }
    @keyframes play-lock-pulse {
      0%, 100% {
        box-shadow:
          0 0 22px rgba(244, 114, 182, 0.85),
          0 0 8px  rgba(244, 114, 182, 0.55) inset;
      }
      50% {
        box-shadow:
          0 0 32px rgba(244, 114, 182, 1),
          0 0 14px rgba(244, 114, 182, 0.85) inset;
      }
    }
    .play-lock span {
      font-family: 'Segoe UI', sans-serif;
      font-size: 1rem;
      font-weight: 800;
      color: #fbd38d;
      letter-spacing: 2.5px;
      text-transform: uppercase;
      text-shadow: 0 0 10px rgba(246, 173, 85, 0.55);
      pointer-events: none;
    }
    /* When a wrap is active, the Keep button flips its inner span to the
       shorthand chord name (CM7, Dm7b5/A, …) instead of "KEEP". Drop the
       uppercase transform — minor 'm' vs major 'M' is meaning-bearing —
       and tighten letter-spacing so longer chord names (slash inversions)
       don't crash into the button edge. */
    .play-lock.show-chord span {
      letter-spacing: 0.8px;
      text-transform: none;
      font-size: 1.05rem;
    }

    /* Keep + Wrap + Rest as a single full-width pill. Three equal grid
       columns (1fr 1fr 1fr) guarantee the buttons stay the same size
       regardless of label length. Outer corners rounded; the two inner
       meeting edges are flat so the trio reads as one component. The
       parent bar's 16px horizontal padding keeps the same outside
       gutters Keep and Rest had as standalone clusters. */
    .kwr-pair {
      display: grid;
      grid-template-columns: 1fr 1fr 1fr;
      align-items: center;
      width: 100%;
      min-width: 0;
    }
    .kwr-pair > button,
    .kwr-pair > .wrap-segment > button {
      width: 100%;
      min-width: 0;
      height: 52px;
      box-sizing: border-box;
      padding: 0 8px;
      /* Shrink the text so KEEP's wider letter-spacing stops crowding
         the inner edges on narrower screens. */
      font-size: 0.85rem;
      letter-spacing: 1.5px;
    }
    /* Middle cell of the KEEP / WRAP / REST grid is a flex row so the
       Edit pill can dock to Wrap's right side at 25% width when the
       wrap is active. When Edit is hidden, Wrap spans the full cell.
       The -3px vertical pull-out lives on this WRAPPER so the inner
       Wrap and Edit buttons can fill it cleanly (no per-child margins
       to keep in lockstep). Net visual height matches KEEP / REST. */
    .kwr-pair .wrap-segment {
      display: flex;
      flex-direction: row;
      align-items: stretch;
      width: 100%;
      min-width: 0;
      height: 52px;
      margin: -3px 0;
    }
    .kwr-pair .wrap-segment > #chord-btn {
      flex: 1 1 auto;
      min-width: 0;
      /* Cancel chord-btn's own -3px pull (defined below for the
         no-segment case); the wrapper handles vertical pull now and
         a doubled pull would extend the button past KEEP/REST. */
      margin: 0;
      height: 100%;
    }
    .kwr-pair .wrap-segment > #wrap-edit-btn {
      flex: 0 0 25%;
      min-width: 0;
      height: 100%;
      box-sizing: border-box;
      padding: 0 4px;
      border: 2px solid #4fd1c5;
      border-left: none;
      border-radius: 0;
      background: rgba(79, 209, 197, 0.10);
      color: #b2f5ea;
      font-family: 'Segoe UI', sans-serif;
      font-size: 0.78rem;
      font-weight: 700;
      letter-spacing: 1px;
      cursor: pointer;
      transition: background 0.15s ease, color 0.15s ease;
    }
    @media (hover: hover) {
      .kwr-pair .wrap-segment > #wrap-edit-btn:hover {
        background: rgba(79, 209, 197, 0.22);
        color: #e6fffa;
      }
    }
    .kwr-pair .wrap-segment > #wrap-edit-btn[hidden] { display: none; }
    /* Mobile: KEEP / WRAP / REST cells get tight on a ~375px viewport.
       The Wrap text + active button glow can crowd the inline Edit pill
       to the point where its label clips. Bump Edit to 32% of the
       segment (still smaller than Wrap) and drop the font size so
       "Edit" fits without truncating. chord-btn's letter-spacing tightens
       so "Wrap" / "Unwrap" / "Close" don't overflow the remaining 68%. */
    @media (max-width: 600px) {
      .kwr-pair > button,
      .kwr-pair > .wrap-segment > button {
        font-size: 0.78rem;
        letter-spacing: 0.8px;
        padding: 0 4px;
      }
      .kwr-pair .wrap-segment > #wrap-edit-btn {
        flex: 0 0 32%;
        font-size: 0.7rem;
        letter-spacing: 0.5px;
      }
    }
    /* Left segment — KEEP. Outer-left rounded, inner-right flat. No
       negative margins — each segment fills its 1fr grid cell so the
       outer edges line up with KEEP/REST exactly; adjacent borders
       sit flush (no overlap, no gap) at the junctions. */
    .kwr-pair .play-lock {
      border-top-right-radius: 0;
      border-bottom-right-radius: 0;
      flex: none;
    }
    .kwr-pair .play-lock span {
      font-size: 0.85rem;
      letter-spacing: 1.5px;
    }
    .kwr-pair .play-lock.show-chord span {
      font-size: 0.95rem;
      letter-spacing: 0.5px;
    }
    /* Middle segment — WRAP. Both inner edges flat. */
    .kwr-pair #chord-btn {
      background: #000;
      border: 2px solid #4fd1c5;
      border-radius: 0;
      display: inline-flex;
      align-items: center;
      justify-content: center;
      cursor: pointer;
      user-select: none;
      font-family: 'Segoe UI', sans-serif;
      font-weight: 800;
      color: #b2f5ea;
      text-transform: uppercase;
      text-shadow: 0 0 10px rgba(79, 209, 197, 0.55);
      box-shadow: 0 0 12px rgba(79, 209, 197, 0.18);
      transition: background 0.08s ease, border-color 0.12s ease, box-shadow 0.12s ease;
      /* Match KEEP's vertical pull-out (-3px) without the horizontal
         negative margin — the grid cell already constrains the width
         so left/right edges land exactly on the track lines. */
      margin: -3px 0;
    }
    .kwr-pair #chord-btn:hover {
      border-color: #81e6d9;
      color: #ccfbf1;
      box-shadow: 0 0 16px rgba(79, 209, 197, 0.32);
    }
    .kwr-pair #chord-btn.active {
      background: rgba(159, 122, 234, 0.22);
      border-color: #c4b5fd;
      color: #ede9fe;
      text-shadow: 0 0 12px rgba(196, 181, 253, 0.95);
      box-shadow:
        0 0 22px rgba(159, 122, 234, 0.85),
        0 0 8px  rgba(159, 122, 234, 0.55) inset;
    }
    /* Right segment — REST. Outer-right rounded, inner-left flat.
       Override the standalone .rest-bar's flex/min-width/padding so it
       sits in its grid cell at the same size as KEEP and WRAP. */
    .kwr-pair .rest-bar {
      flex: none;
      min-width: 0;
      padding: 0 8px;
      border-top-left-radius: 0;
      border-bottom-left-radius: 0;
    }
    .kwr-pair .rest-bar span {
      font-size: 0.85rem;
      letter-spacing: 1.5px;
    }
    /* New strip below the sequence chip view: Grid menu + Step Mode
       button. Sits above the footer-bar (Step controls). */
    .seq-grid-bar {
      background: #050510;
      border-bottom: 1px solid #1a1a2e;
      display: flex;
      align-items: center;
      justify-content: center;
      gap: 18px;
      padding: 6px 16px;
      /* Same protection as .clear-riff-row — keep the Play / Rec / Loop /
         Grid controls at their natural height regardless of how tall the
         sequence display grows. */
      flex-shrink: 0;
    }
    .step-grid-bar-label {
      display: inline-flex;
      align-items: center;
      gap: 6px;
      color: #81e6d9;
      font-family: 'Segoe UI', sans-serif;
      font-size: 0.78rem;
      font-weight: 600;
      letter-spacing: 0.5px;
    }
    /* Grid dimension inputs (rows × cols). Two narrow numeric fields
       with a centered ×; matches the teal palette of the surrounding
       chrome. */
    .grid-dim-input {
      display: inline-flex;
      align-items: center;
      gap: 4px;
    }
    .grid-dim-input select {
      width: 44px;
      border: 1px solid #4fd1c5;
      color: #b2f5ea;
      background: #1a1a2e;
      border-radius: 6px;
      padding: 3px 4px;
      font-size: 0.8rem;
      font-family: 'Segoe UI', sans-serif;
      text-align: center;
      cursor: pointer;
    }
    .grid-dim-input select:focus {
      outline: none;
      border-color: #81e6d9;
    }
    .grid-dim-input select:disabled {
      opacity: 0.45;
      cursor: not-allowed;
      border-color: #2d2d3f;
      color: #4a5568;
    }
    .grid-dim-input select option { background: #1a1a2e; color: #b2f5ea; }
    .grid-dim-sep {
      color: #81e6d9;
      font-size: 0.85rem;
      font-weight: 700;
      padding: 0 1px;
    }
    /* Larger Free/Fixed pill — bigger horizontal presence + heavier
       text without exceeding the row height set by the surrounding
       0.8rem dropdowns (vertical padding stays at 3px to match). */
    #step-mode-btn {
      padding: 3px 22px;
      border: 1px solid #4fd1c5;
      border-radius: 14px;
      background: transparent;
      color: #81e6d9;
      font-family: 'Segoe UI', sans-serif;
      font-size: 0.85rem;
      font-weight: 700;
      letter-spacing: 0.5px;
      cursor: pointer;
      transition: all 0.15s ease;
    }
    #step-mode-btn:hover {
      border-color: #81e6d9;
      color: #b2f5ea;
    }
    #step-mode-btn.active {
      background: rgba(79, 209, 197, 0.18);
      border-color: #81e6d9;
      color: #b2f5ea;
      box-shadow: 0 0 12px rgba(79, 209, 197, 0.4);
    }
    .btn-group--step .step-multi-toggle {
      display: inline-flex;
      align-items: center;
      gap: 6px;
      color: #81e6d9;
      font-family: 'Segoe UI', sans-serif;
      font-size: 0.78rem;
      font-weight: 600;
      cursor: pointer;
      user-select: none;
      padding: 0 6px;
    }
    .btn-group--step .step-multi-toggle input { cursor: pointer; }

    /* Undo toast — fades in at the top of the page when an action is undone. */
    #undo-toast {
      position: fixed;
      top: 16px;
      left: 50%;
      transform: translateX(-50%) translateY(-8px);
      background: #1a1a2e;
      border: 1px solid #00bfff;
      color: #00bfff;
      padding: 9px 18px;
      border-radius: 999px;
      font-family: 'Segoe UI', sans-serif;
      font-size: 0.82rem;
      font-weight: 600;
      z-index: 9999;
      opacity: 0;
      pointer-events: none;
      transition: opacity 0.2s ease, transform 0.2s ease;
      box-shadow: 0 6px 20px rgba(0, 191, 255, 0.25);
    }
    #undo-toast.show {
      opacity: 1;
      transform: translateX(-50%) translateY(0);
    }

    #clear-btn {
      padding: 6px 18px;
      border: 1px solid #2d2d3f;
      border-radius: 20px;
      background: transparent;
      color: #a0aec0;
      font-family: 'Segoe UI', sans-serif;
      font-size: 0.8rem;
      font-weight: 600;
      cursor: pointer;
      transition: all 0.15s ease;
    }
    /* Hover-tinted red, but only on devices with a real hover state —
       on touch the tap-then-release would otherwise leave the button
       stuck red until the next interaction. :active still flashes red
       while the press is held so desktop and touch both get the same
       press feedback. */
    @media (hover: hover) {
      #clear-btn:hover { border-color: #e53e3e; color: #e53e3e; }
    }
    #clear-btn:active { border-color: #e53e3e; color: #e53e3e; }
    #clear-btn:focus { outline: none; }

    #save-btn {
      padding: 6px 18px;
      border: 1px solid #2d2d3f;
      border-radius: 20px;
      background: transparent;
      color: #a0aec0;
      font-family: 'Segoe UI', sans-serif;
      font-size: 0.8rem;
      font-weight: 600;
      cursor: pointer;
      transition: all 0.15s ease;
    }
    #save-btn:hover { border-color: #4299e1; color: #4299e1; }
    #save-btn:disabled { opacity: 0.3; cursor: not-allowed; }

    #save-btn.flash-confirm { border-color: #68d391; color: #68d391; }
    /* Save button while editing a subsequence — matches the green of the
       subedit banner so the action's scope is clear without renaming. */
    #save-btn.sub-edit {
      border-color: #38a169;
      background: rgba(56, 161, 105, 0.18);
      color: #68d391;
    }
    #save-btn.sub-edit:hover { border-color: #68d391; color: #b2f5ea; }

    #chord-btn,
    #rec-btn,
    #random-btn,
    #seed-btn,
    #reverse-btn,
    #shuffle-btn,
    #repeat-btn {
      padding: 6px 18px;
      border: 1px solid #2d2d3f;
      border-radius: 20px;
      background: transparent;
      color: #a0aec0;
      font-family: 'Segoe UI', sans-serif;
      font-size: 0.8rem;
      font-weight: 600;
      cursor: pointer;
      transition: all 0.15s ease;
    }
    #chord-btn:hover { border-color: #9f7aea; color: #9f7aea; }
    #chord-btn.active {
      border-color: #9f7aea;
      background: rgba(159,122,234,0.15);
      color: #d6bcfa;
    }
    /* Rec: red outline + red text by default. When recording, the colors invert
       — fill becomes red, border and text adopt the original (transparent) fill —
       producing a solid red blob that pulses. */
    #rec-btn {
      border-color: #e53e3e;
      color: #e53e3e;
    }
    #rec-btn:hover {
      border-color: #fc8181;
      color: #fc8181;
    }
    #random-btn:hover { border-color: #ed8936; color: #fbd38d; }
    #seed-btn:hover { border-color: #b794f4; color: #d6bcfa; }
    #reverse-btn:hover { border-color: #4299e1; color: #4299e1; }
    #reverse-btn:disabled { opacity: 0.3; cursor: not-allowed; }
    #shuffle-btn:hover { border-color: #4299e1; color: #4299e1; }
    #shuffle-btn:disabled { opacity: 0.3; cursor: not-allowed; }
    #repeat-btn:hover { border-color: #4299e1; color: #4299e1; }
    #repeat-btn:disabled { opacity: 0.3; cursor: not-allowed; }
    #rec-btn.recording {
      background: #e53e3e;
      border-color: #e53e3e;
      color: #fff;
      animation: rec-pulse 1s ease-in-out infinite;
    }
    #rec-btn.counting {
      border-color: #f6ad55;
      color: #fbd38d;
      animation: rec-pulse 0.5s ease-in-out infinite;
    }
    @keyframes rec-pulse {
      0%, 100% { box-shadow: 0 0 0 0 rgba(229,62,62,0.6); }
      50%      { box-shadow: 0 0 0 6px rgba(229,62,62,0); }
    }
    /* Undo / Redo pill — two adjacent buttons sharing the inner border, with
       only their outside corners rounded. Red theme matches Rec. Sized to
       content so the Riff pill sits immediately to its right instead of
       being pushed to the far edge of the row. */
    .btn-group--seq {
      flex-wrap: nowrap;
    }
    .btn-group--seq > .undo-redo-pair {
      flex: 0 0 auto;
      display: flex;
    }
    .undo-redo-pair > button {
      flex: 1;
      padding: 6px 14px;
      border: 1px solid #e53e3e;
      background: transparent;
      color: #e53e3e;
      font-family: 'Segoe UI', sans-serif;
      font-size: 0.8rem;
      font-weight: 600;
      cursor: pointer;
      transition: all 0.15s ease;
    }
    .undo-redo-pair > #undo-btn {
      border-radius: 4px 0 0 4px;
    }
    .undo-redo-pair > #redo-btn {
      border-radius: 0 4px 4px 0;
      border-color: #68d391;
      color: #68d391;
    }
    .undo-redo-pair > button:disabled {
      opacity: 0.3;
      cursor: not-allowed;
    }
    @media (hover: hover) {
      .undo-redo-pair > #undo-btn:hover:not(:disabled) {
        border-color: #fc8181;
        color: #fc8181;
      }
      .undo-redo-pair > #redo-btn:hover:not(:disabled) {
        border-color: #9ae6b4;
        color: #9ae6b4;
      }
    }

    .saved-block.audio-item {
      border-color: #e53e3e;
    }
    .saved-block.audio-item.active-seq {
      background: linear-gradient(135deg, #3a1a2a, #5a1e3a);
      border-color: #fc8181;
    }
    .track-item.audio-item {
      background: linear-gradient(135deg, #4a1a2a, #5a2a3a);
      border-color: #e53e3e;
    }
    .track-item.audio-item.active {
      background: linear-gradient(135deg, #b13a50, #e53e3e);
      border-color: #fc8181;
    }

    .note-length-label {
      display: flex; align-items: center; gap: 4px;
      color: #a0aec0; font-family: 'Segoe UI', sans-serif;
      font-size: 0.8rem; flex-shrink: 0;
    }
    #note-length,
    #subdivision-select {
      width: 60px;
      background: #1a1a2e; border: 1px solid #2d2d3f;
      border-radius: 6px; color: #e2e8f0;
      font-size: 0.8rem; font-family: 'Segoe UI', sans-serif;
      padding: 3px 6px; text-align: center;
    }
    #note-length:focus,
    #subdivision-select:focus { outline: none; border-color: #4299e1; }

    /* Loop button — strictly two states. Inactive: muted grey,
       unchanged on hover/focus so the button doesn't look like there's
       a third "almost-on" state. Active: teal fill + glow. */
    #loop-btn {
      padding: 6px 18px;
      border: 1px solid #2d2d3f;
      border-radius: 20px;
      background: transparent;
      color: #a0aec0;
      font-family: 'Segoe UI', sans-serif;
      font-size: 0.8rem;
      font-weight: 600;
      cursor: pointer;
      transition: background 0.15s ease, color 0.15s ease, border-color 0.15s ease, box-shadow 0.15s ease;
    }
    #loop-btn:hover, #loop-btn:focus { outline: none; }
    #loop-btn.active {
      border-color: #38b2ac;
      background: rgba(56, 178, 172, 0.18);
      color: #81e6d9;
      box-shadow: 0 0 12px rgba(56, 178, 172, 0.45);
    }

    .seq-step.chord {
      background: rgba(159,122,234,0.18);
      border-color: #9f7aea;
      color: #d6bcfa;
      min-width: 48px;
    }
    .seq-step.chord.active {
      background: linear-gradient(135deg, #553c9a, #9f7aea);
      border-color: #9f7aea;
      color: #fff;
    }
    .seq-step.chord-pending {
      background: rgba(159,122,234,0.08);
      border: 1px dashed #9f7aea;
      color: #9f7aea;
      min-width: 48px;
    }

    /* SAVED SEQUENCES VIEWER */
    .saved-viewer {
      background: #050510;
      border-top: 3px solid #000;
      padding: 14px 20px;
      overflow-y: auto;
      max-height: 160px;
    }
    /* Inline action bar — sits above the bank grid and below the
       footer transport when a saved sequence is selected. Mirrors
       the press-and-hold context-menu options for one-tap access.
       Wraps so it never forces horizontal scroll. */
    .saved-actions-bar {
      display: flex;
      flex-wrap: wrap;
      align-items: center;
      gap: 6px;
      padding: 0 2px 10px;
      margin-bottom: 4px;
      border-bottom: 1px solid #1a1a2e;
    }
    .saved-actions-bar[hidden] { display: none; }
    .saved-actions-label {
      font-family: 'Segoe UI', sans-serif;
      font-size: 0.74rem;
      font-weight: 700;
      color: #b3d4ff;
      max-width: 40%;
      overflow: hidden;
      text-overflow: ellipsis;
      white-space: nowrap;
      margin-right: 6px;
    }
    .saved-actions-btn {
      flex: 0 0 auto;
      padding: 5px 10px;
      background: transparent;
      border: 1px solid #2d2d3f;
      border-radius: 14px;
      color: #cbd5e0;
      font-family: 'Segoe UI', sans-serif;
      font-size: 0.72rem;
      font-weight: 600;
      cursor: pointer;
      transition: border-color 0.12s ease, color 0.12s ease;
    }
    @media (hover: hover) {
      .saved-actions-btn:hover { border-color: #4299e1; color: #fff; }
    }
    .saved-actions-btn.danger { color: #fc8181; }
    @media (hover: hover) {
      .saved-actions-btn.danger:hover { border-color: #e53e3e; color: #fff; }
    }

    .saved-grid {
      display: grid;
      grid-template-columns: repeat(6, 1fr);
      gap: 8px;
    }

    /* Centered "Clear bank" pill below the saved-sequence grid. Uses
       50% of the saved-viewer's content width so it reads as a single
       deliberate action, not part of the bank itself. */
    .clear-bank-row {
      display: flex;
      justify-content: center;
      padding: 12px 0 4px;
    }
    .clear-bank-row > #clear-bank-btn {
      width: 50%;
      padding: 8px 16px;
      border: 1px solid #2d2d3f;
      border-radius: 20px;
      background: transparent;
      color: #a0aec0;
      font-family: 'Segoe UI', sans-serif;
      font-size: 0.8rem;
      font-weight: 600;
      cursor: pointer;
      transition: all 0.15s ease;
    }
    @media (hover: hover) {
      .clear-bank-row > #clear-bank-btn:hover { border-color: #e53e3e; color: #e53e3e; }
    }
    .clear-bank-row > #clear-bank-btn:active { border-color: #e53e3e; color: #e53e3e; }
    .clear-bank-row > #clear-bank-btn:disabled { opacity: 0.35; cursor: not-allowed; }
    .clear-bank-row > #clear-bank-btn:focus { outline: none; }

    .saved-block {
      background: #1a1a2e;
      border: 1px solid #2d2d3f;
      border-radius: 8px;
      padding: 8px 4px;
      display: flex;
      flex-direction: column;
      align-items: center;
      justify-content: center;
      cursor: pointer;
      transition: all 0.15s ease;
      gap: 3px;
      user-select: none;
      -webkit-user-select: none;
      -webkit-touch-callout: none;
    }
    .saved-block:hover { border-color: #4299e1; background: #1e2a3a; }
    .saved-block.dragging { opacity: 0.35; }
    .saved-block.drag-over { outline: 2px solid #4299e1; }
    .seq-step.dragging { opacity: 0.35; cursor: grabbing; }
    .seq-step.drag-over { outline: 2px solid #4299e1; outline-offset: -1px; }
    /* Pitch bend indicator — small arrow in the chip's top-right corner so
       a bending step is recognizable at a glance without crowding the label. */
    .seq-step.bend-up::after,
    .seq-step.bend-down::after {
      position: absolute;
      top: 2px;
      right: 4px;
      font-size: 0.7rem;
      font-weight: 700;
      color: #f6ad55;
      pointer-events: none;
    }
    .seq-step.bend-up::after   { content: '↗'; }
    .seq-step.bend-down::after { content: '↘'; }
    .saved-block.active-seq { border-color: #4299e1; background: linear-gradient(135deg, #1a2a4a, #1e3a5a); }

    .saved-block-name {
      font-family: 'Segoe UI', sans-serif;
      font-size: 0.85rem;
      font-weight: 700;
      color: #e2e8f0;
    }
    .saved-block-count {
      font-family: 'Segoe UI', sans-serif;
      font-size: 0.65rem;
      color: #4a4a6a;
    }

    /* PLAYLIST BAR */
    /* TRACKS SECTION */
    .tracks-section {
      background: #050510;
      border-top: 3px solid #000;
      padding: 10px 16px;
      overflow-y: auto;
      max-height: 240px;
      /* Positioning context for the .tracks-play-cursor, which sits
         on top of the tracks and tracks the current playback head. */
      position: relative;
    }
    /* Play cursor — narrow vertical line that follows the playback
       head across every track. Driven by JS each animation frame
       while a track is playing; static otherwise (stays at the user's
       last click / seek position). */
    .tracks-play-cursor {
      position: absolute;
      width: 2px;
      background: rgba(252, 211, 77, 0.95);
      box-shadow: 0 0 6px rgba(252, 211, 77, 0.6);
      pointer-events: none;
      z-index: 6;
      border-radius: 1px;
    }
    .tracks-play-cursor[hidden] { display: none; }
    /* The ruler also acts as a seek target — show a crosshair so it's
       obvious you can click anywhere on the time grid to position the
       playback head. Excludes the actual handles, which still own
       their drag pointers. */
    .tracks-loop-ruler { cursor: pointer; }
    .tracks-loop-ruler .tracks-loop-handle { cursor: ew-resize; }
    /* Mixdown — Export + Share live in their own row below tracks so they
       don't compete with per-track controls in the tracks header. */
    .mixdown-section {
      background: #050510;
      border-top: 3px solid #000;
      padding: 8px 16px;
      display: flex;
      align-items: center;
      gap: 10px;
      flex-wrap: wrap;
    }
    .mixdown-label {
      color: #a0aec0;
      font-family: 'Segoe UI', sans-serif;
      font-size: 0.78rem;
      font-weight: 700;
      text-transform: uppercase;
      letter-spacing: 1px;
      margin-right: 6px;
    }
    .tracks-header {
      display: flex; align-items: center; gap: 10px; margin-bottom: 10px;
      flex-wrap: wrap;
    }
    /* Zoom controls live in the tracks-header next to the fullscreen
       button. Compact pills so they don't crowd the row, with a
       monospace percent readout between them. */
    .tracks-header #tracks-zoom-in,
    .tracks-header #tracks-zoom-out {
      flex: 0 0 auto;
      padding: 2px 10px;
      background: transparent;
      border: 1px solid #2d2d3f;
      border-radius: 12px;
      color: #cbd5e0;
      font-family: 'Segoe UI', sans-serif;
      font-size: 0.9rem;
      font-weight: 700;
      line-height: 1;
      cursor: pointer;
    }
    @media (hover: hover) {
      .tracks-header #tracks-zoom-in:hover,
      .tracks-header #tracks-zoom-out:hover {
        border-color: #4299e1; color: #fff;
      }
    }
    .tracks-header #tracks-zoom-label {
      flex: 0 0 auto;
      min-width: 36px;
      font-family: 'SF Mono', 'Menlo', monospace;
      font-size: 0.72rem;
      color: #a0aec0;
      text-align: center;
    }
    /* Mobile: tighten the transport row so buttons fit and wrap cleanly,
       and shorten the multi-word labels so a row of pill buttons doesn't
       blow out the viewport. */
    @media (max-width: 700px) {
      .tracks-header {
        gap: 6px;
        margin-bottom: 8px;
      }
      .tracks-header #tracks-title-btn {
        flex: 0 1 auto;
        font-size: 0.78rem;
        padding: 0;
      }
      .tracks-header #tracks-play-all {
        width: 30px; height: 30px; min-width: 30px; min-height: 30px;
        font-size: 0.85rem;
      }
      .tracks-header #add-track-btn,
      .tracks-header #tracks-loop-all,
      .tracks-header #tracks-back-btn {
        padding: 4px 9px;
        font-size: 0.7rem;
        letter-spacing: 0;
      }
      .tracks-header #add-track-btn { font-size: 0; }
      .tracks-header #add-track-btn::before {
        content: '+ New'; font-size: 0.7rem;
      }
    }
    #tracks-title-btn {
      flex: 1;
      text-align: left;
      background: transparent;
      border: none;
      color: #a0aec0;
      font-family: 'Segoe UI', sans-serif;
      font-size: 0.85rem;
      font-weight: 700;
      cursor: pointer;
      padding: 4px 0;
      transition: color 0.15s ease;
    }
    #tracks-title-btn:hover { color: #4299e1; }
    #tracks-back-btn {
      display: none;
      padding: 5px 12px;
      border: 1px solid #2d2d3f;
      border-radius: 16px;
      background: transparent;
      color: #a0aec0;
      font-family: 'Segoe UI', sans-serif;
      font-size: 0.78rem;
      font-weight: 600;
      cursor: pointer;
      transition: all 0.15s ease;
    }
    #tracks-back-btn:hover { border-color: #4299e1; color: #4299e1; }

    /* Fullscreen tracks view — triggered by tapping the Tracks title.
       Hides every sibling and pins the tracks section to the viewport so
       a phone held lengthwise shows the whole tracks grid edge-to-edge. */

    /* Tracks-fullscreen: only the tracks-section itself shows. .tracks-
       section now lives inside #mix-view, so we let #mix-view through at
       the top level (forced display:block since it's normally hidden in
       Make view), then hide every #mix-view child except the tracks
       section so the Mixdown bar collapses too. */
    body.tracks-fullscreen > *:not(#mix-view):not(.ctx-menu) { display: none !important; }
    body.tracks-fullscreen > #mix-view { display: block !important; padding: 0; }
    body.tracks-fullscreen #mix-view > *:not(.tracks-section) { display: none !important; }
    body.tracks-fullscreen {
      overflow: hidden;
      width: 100vw;
      height: 100vh;
      height: 100dvh; /* track the visible viewport on iOS so the URL bar
                         collapsing in landscape doesn't clip content */
    }
    body.tracks-fullscreen .tracks-section {
      position: fixed;
      top: 0;
      left: 0;
      right: 0;
      bottom: 0;
      width: 100vw;
      height: 100vh;
      height: 100dvh;
      max-height: none;
      z-index: 1000;
      overflow-y: auto;
      -webkit-overflow-scrolling: touch;
      padding:
        max(14px, env(safe-area-inset-top))
        max(20px, env(safe-area-inset-right))
        max(14px, env(safe-area-inset-bottom))
        max(20px, env(safe-area-inset-left));
    }
    /* Pin the header to the top of the scrollable area so Back / Play / Add
       Track stay reachable in landscape where vertical room is tight. */
    body.tracks-fullscreen .tracks-header {
      position: sticky;
      top: 0;
      background: #050510;
      padding: 4px 0 8px;
      margin-bottom: 8px;
      z-index: 5;
      border-bottom: 1px solid #1a1a2e;
    }
    body.tracks-fullscreen #tracks-back-btn { display: inline-block; }
    body.tracks-fullscreen #tracks-title-btn { display: none; }
    #tracks-play-all {
      background: #000;
      color: #22ff88;
      border: 2px solid #22ff88;
      box-shadow: 0 0 12px rgba(34, 255, 136, 0.45);
      width: 34px; height: 34px; min-width: 34px; min-height: 34px;
    }
    #tracks-play-all:hover {
      box-shadow: 0 0 18px rgba(34, 255, 136, 0.7);
    }
    #add-track-btn,
    #tracks-export-btn,
    #tracks-share-btn,
    #tracks-countin-btn,
    #tracks-loop-all {
      padding: 5px 12px;
      border: 1px solid #2d2d3f;
      border-radius: 16px;
      background: transparent;
      color: #a0aec0;
      font-family: 'Segoe UI', sans-serif;
      font-size: 0.78rem; font-weight: 600;
      cursor: pointer;
      transition: all 0.15s ease;
    }
    #add-track-btn:hover { border-color: #4299e1; color: #4299e1; }
    #tracks-export-btn:hover { border-color: #38a169; color: #38a169; }
    #tracks-export-btn:disabled { opacity: 0.5; cursor: not-allowed; }
    #tracks-share-btn:hover { border-color: #f6ad55; color: #fbd38d; }
    #tracks-share-btn:disabled { opacity: 0.5; cursor: not-allowed; }
    #tracks-countin-btn:hover { border-color: #e53e3e; color: #fc8181; }
    #tracks-countin-btn.active {
      border-color: #e53e3e;
      background: rgba(229, 62, 62, 0.18);
      color: #fc8181;
    }
    #tracks-loop-all:hover { border-color: #38b2ac; color: #38b2ac; }
    #tracks-loop-all.active {
      border-color: #38b2ac;
      background: rgba(56,178,172,0.15);
      color: #81e6d9;
    }
    /* Multi-select toggle — one on every track-controls row, all
       wired to the same _tracksMultiSelect flag so flipping any
       checkbox flips them all. Pill chrome matches the other track
       buttons (Rec / Stereo / Mix / Clear / ×). The native checkbox
       is hidden but kept for accessibility / keyboard; the label is
       the clickable pill and gains a magenta tint when checked so
       the user can spot Multi mode at a glance. */
    .track-multi-toggle {
      display: inline-flex;
      align-items: center;
      padding: 2px 8px;
      border: 1px solid #2d2d3f;
      border-radius: 12px;
      background: transparent;
      color: #a0aec0;
      font-family: 'Segoe UI', sans-serif;
      font-size: 0.66rem; font-weight: 600;
      letter-spacing: 0.5px;
      cursor: pointer;
      user-select: none;
      transition: border-color 0.15s ease, color 0.15s ease, background 0.15s ease;
    }
    .track-multi-toggle input[type="checkbox"] {
      position: absolute;
      width: 1px; height: 1px;
      opacity: 0;
      pointer-events: none;
    }
    @media (hover: hover) {
      .track-multi-toggle:hover { border-color: #d91e7c; color: #f687b3; }
    }
    .track-multi-toggle:has(input:checked) {
      border-color: #d91e7c;
      background: rgba(217, 30, 124, 0.18);
      color: #f687b3;
    }
    .track-row {
      display: block;
      margin-bottom: 8px;
    }
    /* Track controls now sit ABOVE the grid in their own row. The
       grid below spans the full width of .tracks-section so the
       sequence chips align with the left screen edge (instead of
       being offset by a controls column). */
    .track-controls {
      display: flex; align-items: center; gap: 4px;
      margin-bottom: 4px;
      flex-wrap: wrap;
    }
    /* Don't let the track name stretch across the row — keep the
       whole controls cluster (name + buttons) tight against the left
       edge so it lines up with the grid below. */
    .track-controls .track-name {
      flex: 0 0 auto;
      min-width: 0;
    }
    .track-controls button {
      padding: 4px 8px;
      border: 1px solid #2d2d3f;
      border-radius: 12px;
      background: transparent;
      color: #a0aec0;
      font-size: 0.72rem;
      font-family: 'Segoe UI', sans-serif;
      cursor: pointer;
      transition: all 0.15s;
    }
    .track-play { min-width: 30px; font-size: 0.8rem; }
    .track-play.playing { border-color: #4299e1; color: #4299e1; background: rgba(66,153,225,0.1); }
    .track-loop.active { border-color: #38b2ac; color: #81e6d9; background: rgba(56,178,172,0.15); }
    .track-remove:hover { border-color: #e53e3e; color: #e53e3e; }
    .track-clear { font-size: 0.66rem !important; padding: 2px 8px !important; letter-spacing: 0.5px; }
    .track-clear:hover { border-color: #e53e3e; color: #e53e3e; }
    .track-eq { font-size: 0.66rem !important; padding: 2px 8px !important; letter-spacing: 0.5px; }
    .track-eq:hover { border-color: #f6ad55; color: #fbd38d; }
    .track-eq.active { border-color: #f6ad55; color: #fbd38d; background: rgba(246,173,85,0.12); }
    .track-stereo {
      font-size: 0.66rem !important; padding: 2px 6px !important; letter-spacing: 0.5px;
      min-width: 24px;
      border-color: #4299e1 !important; color: #90cdf4 !important;
      background: rgba(66, 153, 225, 0.10) !important;
    }
    .track-stereo:hover { border-color: #63b3ed !important; color: #bee3f8 !important; }
    .track-stereo.mono {
      border-color: #d69e2e !important; color: #f6e05e !important;
      background: rgba(214, 158, 46, 0.10) !important;
    }
    .track-stereo.mono:hover { border-color: #ecc94b !important; color: #faf089 !important; }
    .track-rec {
      font-size: 0.66rem !important; padding: 2px 8px !important; letter-spacing: 0.5px;
      border-color: #e53e3e !important; color: #e53e3e !important;
    }
    .track-rec:hover { border-color: #fc8181 !important; color: #fc8181 !important; }
    .track-rec.recording {
      background: #e53e3e !important;
      border-color: #e53e3e !important;
      color: #fff !important;
      animation: rec-pulse 1s ease-in-out infinite;
    }
    .track-rec.counting {
      border-color: #f6ad55 !important;
      color: #fbd38d !important;
      animation: rec-pulse 0.5s ease-in-out infinite;
    }
    .track-name {
      color: #e2e8f0;
      font-family: 'Segoe UI', sans-serif;
      font-size: 0.76rem; font-weight: 600;
      /* ~20% narrower than the previous 2px 8px chrome — the track
         identifier just holds a 1-2 character index so the wider
         padding was wasting row space. */
      padding: 2px 5px;
      border-radius: 6px;
      border: 1px solid transparent;
      cursor: pointer;
      user-select: none;
      transition: background 0.15s ease, border-color 0.15s ease, color 0.15s ease;
    }
    .track-name:hover { border-color: #38a169; color: #68d391; }
    .track-name.soloed {
      background: rgba(56, 161, 105, 0.18);
      border-color: #38a169;
      color: #68d391;
    }
    /* Loop region ruler — sits above the track grid. Width matches the
       longest track's natural width (set in JS from _maxTracksDurationSec).
       The fill highlights the region between the start + end handles.
       Handles are draggable and soft-lock to item boundary times.
       Background carries a 1-sec minor / N-sec major grid so the user
       can read time positions at a glance. Grid spacing + label
       cadence come from CSS custom properties set by JS. */
    .tracks-loop-ruler {
      position: relative;
      height: 22px;
      margin: 0 0 4px;
      background-color: #0a0a14;
      background-image:
        linear-gradient(to right, rgba(160,174,192,0.10) 1px, transparent 1px),
        linear-gradient(to right, rgba(160,174,192,0.28) 1px, transparent 1px);
      background-size: var(--ruler-minor, 36px) 100%, var(--ruler-major, 144px) 100%;
      background-position-x: var(--ruler-pad, 4px), var(--ruler-pad, 4px);
      background-repeat: repeat-x;
      border-radius: 4px;
      border: 1px solid #1a1a2e;
      overflow: hidden;
    }
    .tracks-loop-ruler.disabled { opacity: 0.55; }
    .tracks-loop-fill {
      position: absolute;
      top: 0; bottom: 0;
      background: rgba(56, 178, 172, 0.22);
      border-left:  1px solid rgba(56, 178, 172, 0.7);
      border-right: 1px solid rgba(56, 178, 172, 0.7);
      pointer-events: none;
    }
    /* Time labels overlay — populated in JS at every major-tick second.
       Sits above the fill so the numbers are readable inside the loop
       region too. */
    .tracks-loop-labels {
      position: absolute;
      top: 0; left: 0; right: 0; bottom: 0;
      pointer-events: none;
    }
    .tracks-loop-label {
      position: absolute;
      top: 2px;
      transform: translateX(2px);
      font-family: 'SF Mono', 'Menlo', monospace;
      font-size: 0.6rem;
      color: rgba(160, 174, 192, 0.75);
      line-height: 1;
      letter-spacing: 0.5px;
      pointer-events: none;
    }
    .tracks-loop-handle {
      position: absolute;
      top: 0; bottom: 0;
      width: 10px;
      transform: translateX(-50%);
      background: #38b2ac;
      border-radius: 3px;
      cursor: ew-resize;
      touch-action: none;
      box-shadow: 0 0 0 1px #0a0a14 inset;
      z-index: 2;
    }
    .tracks-loop-handle:hover,
    .tracks-loop-handle.dragging {
      background: #81e6d9;
      box-shadow: 0 0 0 1px #0a0a14 inset, 0 0 6px rgba(129, 230, 217, 0.6);
    }
    .track-grid {
      display: grid;
      grid-auto-flow: column;
      /* Fixed pixel cells (not 1fr) so the same time-duration takes the same
         visual width across every track row. With 1fr the cells stretched
         per-track, making short tracks appear as wide as long ones; with a
         pinned cell width, longer tracks just extend further into their
         scroll area. The cell width is driven by --track-cell-w (set by
         setTracksZoom) so the Mix zoom in / out buttons shrink or grow
         every track's visual time resolution in lockstep with the loop
         ruler and drop indicator. */
      grid-auto-columns: var(--track-cell-w, 18px);
      grid-template-rows: 44px;
      gap: 2px;
      overflow-x: auto;
      background: #0a0a14;
      border: 1px solid #1a1a2e;
      border-radius: 6px;
      padding: 4px;
      min-height: 52px;
      scrollbar-width: thin;
      scrollbar-color: #2d2d3f #0a0a14;
    }
    .track-grid.drag-over {
      outline: 2px dashed #4299e1;
      outline-offset: -2px;
      background: #0e1324;
    }
    .track-grid::-webkit-scrollbar { height: 4px; }
    .track-grid::-webkit-scrollbar-track { background: #0a0a14; }
    .track-grid::-webkit-scrollbar-thumb { background: #2d2d3f; border-radius: 2px; }
    .track-item {
      background: linear-gradient(135deg, #1a2a4a, #1e3a5a);
      border: 1px solid #2b6cb0;
      border-radius: 4px;
      display: flex; align-items: center; justify-content: center;
      padding: 0 4px;
      color: #e2e8f0;
      font-family: 'Segoe UI', sans-serif;
      font-size: 0.7rem; font-weight: 700;
      white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
      cursor: pointer;
      transition: all 0.15s;
    }
    .track-item:hover { border-color: #4299e1; }
    .track-item.active {
      background: linear-gradient(135deg, #2b6cb0, #4299e1);
      border-color: #4299e1;
    }
    /* Loop region styling — items whose time range falls inside the
       loop region get a subtle teal tint so the user can see what
       will actually loop. Items entirely outside the region dim out
       since they're skipped while masterLoop is on. The styles are
       always painted (regardless of masterLoop state) so the user
       can preview which items will loop before toggling Loop. */
    .track-item.in-loop {
      box-shadow: 0 0 0 1px rgba(56, 178, 172, 0.55) inset,
                  0 0 8px rgba(56, 178, 172, 0.20);
    }
    body.loop-region-active .track-item.out-of-loop {
      opacity: 0.35;
      filter: saturate(0.55);
    }
    /* Selected track item — opens the Item ▾ actions dropdown in
       the tracks header. Distinguished from .active (currently
       playing) with a magenta outline so the two states can stack.
       In multi-select, every selected item carries .selected; the
       most recently clicked one additionally carries .selected-primary
       so the user can tell which item the popover is anchored to. */
    .track-item.selected {
      outline: 2px solid #d91e7c;
      outline-offset: -3px;
      box-shadow: 0 0 12px rgba(217, 30, 124, 0.45);
    }
    .track-item.selected-primary {
      box-shadow: 0 0 16px rgba(217, 30, 124, 0.65), inset 0 0 0 1px #d91e7c;
    }
    /* Silent placeholder — left behind when a saved sequence is deleted so
       items after it keep their absolute time position in the track. */
    .track-item.silent-item {
      background: repeating-linear-gradient(
        45deg,
        #1a1a2e,
        #1a1a2e 6px,
        #15151f 6px,
        #15151f 12px
      );
      border: 1px dashed #2d2d3f;
      color: #4a4a6a;
      font-style: italic;
    }
    .track-item.silent-item:hover { border-color: #4a4a6a; }
    /* Track-item edit popover — anchored under a clicked item in Mix.
       Carries every per-item control (volume, duplicate, reverse,
       speed, move, remove) so the user doesn't have to discover a
       separate header button. Position is set in JS via top/left;
       z-index sits above ctx-menu so the two never stack. */
    .track-item-popover {
      position: fixed;
      z-index: 9500;
      min-width: 240px;
      max-width: 300px;
      background: #0a0a14;
      border: 1px solid #2d2d3f;
      border-radius: 8px;
      padding: 10px;
      box-shadow: 0 8px 32px rgba(0,0,0,0.7), 0 0 16px rgba(217, 30, 124, 0.18);
      font-family: 'Segoe UI', sans-serif;
      font-size: 0.78rem;
      color: #cbd5e0;
      display: flex; flex-direction: column; gap: 8px;
    }
    .track-item-popover .tip-head {
      display: flex; align-items: center; justify-content: space-between;
      gap: 8px;
      padding-bottom: 6px;
      border-bottom: 1px solid #2d2d3f;
    }
    .track-item-popover .tip-name {
      color: #f0f4ff;
      font-weight: 700;
      font-size: 0.82rem;
      overflow: hidden;
      text-overflow: ellipsis;
      white-space: nowrap;
      flex: 1 1 auto;
      min-width: 0;
    }
    .track-item-popover .tip-close {
      background: transparent; border: 1px solid #2d2d3f;
      color: #a0aec0; cursor: pointer;
      width: 22px; height: 22px;
      border-radius: 4px;
      display: inline-flex; align-items: center; justify-content: center;
      font-size: 0.9rem; line-height: 1;
    }
    .track-item-popover .tip-close:hover { color: #fff; border-color: #4299e1; }
    .track-item-popover .tip-sub {
      color: #a0aec0;
      font-size: 0.7rem;
      line-height: 1.35;
      padding: 0 2px;
      margin-top: -2px;
    }
    .track-item-popover .tip-row {
      display: flex; align-items: center; gap: 8px;
      flex-wrap: wrap;
    }
    .track-item-popover .tip-label {
      color: #a0aec0;
      font-size: 0.72rem;
      min-width: 56px;
    }
    .track-item-popover .tip-vol {
      flex: 1 1 auto;
      accent-color: #d91e7c;
      min-width: 0;
    }
    .track-item-popover .tip-val {
      color: #b3d4ff;
      font-weight: 700;
      min-width: 36px;
      text-align: right;
      font-variant-numeric: tabular-nums;
    }
    .track-item-popover .tip-btn {
      flex: 1 1 auto;
      min-width: 80px;
      background: transparent;
      border: 1px solid #2d2d3f;
      color: #cbd5e0;
      padding: 6px 10px;
      border-radius: 6px;
      font-size: 0.74rem;
      font-family: inherit;
      cursor: pointer;
      transition: border-color 0.12s ease, color 0.12s ease;
    }
    .track-item-popover .tip-btn:hover { border-color: #4299e1; color: #fff; }
    .track-item-popover .tip-btn.danger { color: #fc8181; }
    .track-item-popover .tip-btn.danger:hover { border-color: #e53e3e; color: #fff; }
    .track-item-popover .tip-btn:disabled {
      opacity: 0.4; cursor: not-allowed; border-color: #1c1c2e; color: #4a4a6a;
    }
    .track-item-popover .tip-select {
      flex: 1 1 auto;
      background: #050510;
      border: 1px solid #2d2d3f;
      color: #cbd5e0;
      border-radius: 6px;
      padding: 4px 6px;
      font-family: inherit;
      font-size: 0.74rem;
      max-width: 100%;
    }
    /* Drag affordances — a dragging item dims; .track-grid lights up
       the drop slot. drop-before is set on the .track-item being
       targeted to show a magenta left-edge indicator. */
    .track-item.dragging { opacity: 0.45; }
    .track-item.drop-before { box-shadow: -3px 0 0 0 #d91e7c inset; }
    .track-item.drop-after  { box-shadow:  3px 0 0 0 #d91e7c inset; }
    /* Drop indicator — a vertical magenta line drawn inside the
       destination track-grid at the snapped drop time. Replaces the
       per-item drop-before / drop-after shadows for arbitrary
       cross-track positioning; the line sits between items or beyond
       the last one so the user can see where the dragged sequence
       will actually land. */
    .track-drop-indicator {
      position: absolute;
      top: 2px; bottom: 2px;
      width: 2px;
      background: #d91e7c;
      box-shadow: 0 0 6px rgba(217, 30, 124, 0.7);
      pointer-events: none;
      z-index: 5;
    }
    .track-empty {
      color: #4a4a6a;
      font-family: 'Segoe UI', sans-serif;
      font-size: 0.72rem;
      padding: 10px;
      grid-column: 1 / -1;
      white-space: nowrap;
    }

    /* LONG-PRESS CONTEXT MENU */
    .ctx-menu {
      position: fixed;
      background: #1a1a2e;
      border: 1px solid #2d2d3f;
      border-radius: 10px;
      box-shadow: 0 8px 24px rgba(0,0,0,0.5);
      /* z-index above the codebase's panel ceiling (9000) so the
         menu paints over modals / FX panels / settings dropdowns
         no matter what's open behind it. */
      z-index: 10001;
      overflow: hidden;
      min-width: 140px;
    }

    .ctx-menu button {
      display: block;
      width: 100%;
      padding: 12px 16px;
      background: none;
      border: none;
      color: #e2e8f0;
      font-family: 'Segoe UI', sans-serif;
      font-size: 0.85rem;
      text-align: left;
      cursor: pointer;
      transition: background 0.15s;
    }
    .ctx-menu button:hover { background: #2d2d3f; }
    .ctx-menu button.danger { color: #fc8181; }
    .ctx-menu button.danger:hover { background: #2d1a1a; }
    .ctx-menu hr { border: none; border-top: 1px solid #2d2d3f; margin: 0; }

    .saved-empty {
      color: #2d2d3f;
      font-family: 'Segoe UI', sans-serif;
      font-size: 0.8rem;
      grid-column: 1 / -1;
    }

    /* Shared base for every modal overlay (Wavetable, Grain, Step-div,
       Pitch-ramp, Variance, etc.). Each variant tags itself with both
       .modal-overlay and a specific .<name>-overlay class; the base
       handles fixed-position centering so variants don't have to
       repeat it. */
    .modal-overlay {
      position: fixed; inset: 0;
      background: rgba(0,0,0,0.72);
      z-index: 1200;
      display: flex; align-items: center; justify-content: center;
      touch-action: manipulation;
    }

    /* STEP-DIV PICKER (Keep flow) — modal that asks for a chip's size
       after a note is added in Spell or Stack mode. */
    .step-div-overlay {
      position: fixed; inset: 0;
      background: rgba(0,0,0,0.72);
      z-index: 1200;
      display: flex; align-items: center; justify-content: center;
      touch-action: manipulation;
    }
    .step-div-modal {
      background: #050510;
      border: 1px solid #2d2d3f;
      border-radius: 14px;
      padding: 18px 18px 16px;
      width: min(360px, 92vw);
      box-shadow: 0 16px 48px rgba(0,0,0,0.65), 0 0 28px rgba(0, 191, 255, 0.06);
    }
    .step-div-modal .sdiv-title {
      font-family: 'Segoe UI', sans-serif;
      font-size: 0.95rem; font-weight: 700;
      color: #f0f4ff; margin-bottom: 14px;
      text-align: center;
    }
    .step-div-modal .sdiv-grid {
      display: grid;
      grid-template-columns: repeat(3, 1fr);
      gap: 8px;
    }
    .step-div-modal .sdiv-opt {
      padding: 12px 8px;
      border: 1px solid #2d2d3f;
      border-radius: 12px;
      background: transparent;
      color: #a0aec0;
      font-family: 'Segoe UI', sans-serif;
      font-size: 0.95rem;
      font-weight: 700;
      cursor: pointer;
      transition: all 0.15s ease;
      touch-action: manipulation;
    }
    @media (hover: hover) {
      .step-div-modal .sdiv-opt:hover { border-color: #4299e1; color: #4299e1; }
    }
    .step-div-modal .sdiv-opt:active { background: rgba(66,153,225,0.18); }
    .step-div-modal .sdiv-lock {
      display: flex; align-items: center; gap: 8px;
      font-family: 'Segoe UI', sans-serif;
      font-size: 0.78rem;
      color: #a0aec0;
      cursor: pointer;
      user-select: none;
      padding: 0 4px;
      margin-bottom: 14px;
    }
    .step-div-modal .sdiv-lock input[type="checkbox"] {
      cursor: pointer;
      accent-color: #4299e1;
    }

    /* PITCH RAMP DIALOG — opens from the step ctx menu, sets the
       step.bend semitones + atFraction the playback path already
       honors. */
    .pitch-ramp-overlay {
      position: fixed; inset: 0;
      background: rgba(0,0,0,0.72);
      z-index: 1200;
      display: flex; align-items: center; justify-content: center;
      touch-action: manipulation;
    }
    .pitch-ramp-modal {
      background: #050510;
      border: 1px solid #2d2d3f;
      border-radius: 14px;
      padding: 18px 18px 16px;
      width: min(380px, 92vw);
      box-shadow: 0 16px 48px rgba(0,0,0,0.65), 0 0 28px rgba(66, 153, 225, 0.10);
    }
    .pitch-ramp-modal .pr-title {
      font-family: 'Segoe UI', sans-serif;
      font-size: 0.95rem; font-weight: 700;
      color: #f0f4ff; margin-bottom: 6px;
      text-align: center;
    }
    .pitch-ramp-modal .pr-sub {
      font-family: 'Segoe UI', sans-serif;
      font-size: 0.78rem;
      color: #a0aec0;
      margin-bottom: 14px;
      text-align: center;
    }
    .pitch-ramp-modal .pr-sub strong { color: #b3d4ff; }
    .pitch-ramp-modal .pr-param { margin-bottom: 12px; padding: 0 4px; }
    .pitch-ramp-modal .pr-row {
      display: flex; justify-content: space-between; align-items: baseline;
      font-family: 'Segoe UI', sans-serif;
      font-size: 0.78rem;
      color: #a0aec0;
      margin-bottom: 4px;
    }
    .pitch-ramp-modal .pr-val {
      color: #b3d4ff;
      font-weight: 700;
    }
    .pitch-ramp-modal input[type="range"] {
      width: 100%;
      accent-color: #4299e1;
    }
    .pitch-ramp-modal .pr-actions {
      display: flex; justify-content: space-between; gap: 8px;
      margin-top: 14px;
    }
    .pitch-ramp-modal .pr-btn {
      flex: 1 1 0;
      padding: 8px 6px;
      border: 1px solid #2d2d3f;
      border-radius: 8px;
      background: transparent;
      color: #a0aec0;
      font-family: 'Segoe UI', sans-serif;
      font-size: 0.82rem;
      font-weight: 700;
      cursor: pointer;
      transition: all 0.15s ease;
      touch-action: manipulation;
    }
    @media (hover: hover) {
      .pitch-ramp-modal .pr-btn:hover { border-color: #4299e1; color: #4299e1; }
    }
    .pitch-ramp-modal .pr-btn.pr-clear:hover { border-color: #e53e3e; color: #e53e3e; }
    .pitch-ramp-modal .pr-btn.pr-save {
      border-color: #4299e1;
      color: #b3d4ff;
      background: rgba(66, 153, 225, 0.18);
    }

    /* VARIANCE PICKER (per-step Random / Linear) */
    .variance-overlay {
      position: fixed; inset: 0;
      background: rgba(0,0,0,0.72);
      z-index: 1200;
      display: flex; align-items: center; justify-content: center;
      touch-action: manipulation;
    }
    .variance-modal {
      background: #050510;
      border: 1px solid #2d2d3f;
      border-radius: 14px;
      padding: 18px 18px 16px;
      width: min(360px, 92vw);
      box-shadow: 0 16px 48px rgba(0,0,0,0.65), 0 0 28px rgba(246, 173, 85, 0.10);
    }
    .variance-modal .vrn-title {
      font-family: 'Segoe UI', sans-serif;
      font-size: 0.95rem; font-weight: 700;
      color: #f0f4ff; margin-bottom: 14px;
      text-align: center;
    }
    .variance-modal .vrn-iters {
      display: flex;
      align-items: center;
      justify-content: space-between;
      gap: 10px;
      font-family: 'Segoe UI', sans-serif;
      font-size: 0.82rem;
      color: #cbd5e0;
      margin-bottom: 10px;
      padding: 0 4px;
    }
    .variance-modal .vrn-iters input[type="number"] {
      width: 64px;
      padding: 4px 6px;
      border: 1px solid #2d2d3f;
      border-radius: 6px;
      background: #1a1a2e;
      color: #e2e8f0;
      font-family: 'Segoe UI', sans-serif;
      font-size: 0.85rem;
      text-align: center;
    }
    .variance-modal .vrn-iters input[type="number"]:focus {
      outline: none;
      border-color: #f6ad55;
    }
    .variance-modal .vrn-random {
      display: flex;
      align-items: flex-start;
      gap: 8px;
      font-family: 'Segoe UI', sans-serif;
      font-size: 0.78rem;
      color: #a0aec0;
      cursor: pointer;
      user-select: none;
      padding: 0 4px 4px;
      margin-bottom: 12px;
      line-height: 1.4;
    }
    .variance-modal .vrn-random input[type="checkbox"] {
      cursor: pointer;
      accent-color: #f6ad55;
      flex: 0 0 auto;
      margin-top: 2px;
    }
    .variance-modal .vrn-grid {
      display: grid;
      grid-template-columns: 1fr 1fr;
      gap: 10px;
      margin-bottom: 12px;
    }
    .variance-modal .vrn-opt {
      padding: 14px 8px;
      border: 1px solid #2d2d3f;
      border-radius: 12px;
      background: transparent;
      color: #a0aec0;
      font-family: 'Segoe UI', sans-serif;
      font-size: 0.95rem;
      font-weight: 700;
      cursor: pointer;
      transition: all 0.15s ease;
      touch-action: manipulation;
    }
    @media (hover: hover) {
      .variance-modal .vrn-opt:hover { border-color: #f6ad55; color: #f6ad55; }
    }
    .variance-modal .vrn-opt:active { background: rgba(246, 173, 85, 0.18); }
    .variance-modal .vrn-hint {
      font-family: 'Segoe UI', sans-serif;
      font-size: 0.72rem;
      color: #718096;
      text-align: center;
      line-height: 1.4;
    }

    /* Variance state on the step chip itself — blinking outline while
       the pool is being edited; a small ⟳ glyph for committed steps. */
    @keyframes variance-blink {
      0%, 100% { opacity: 1; }
      50%      { opacity: 0.35; }
    }
    .seq-step.variance-editing {
      animation: variance-blink 0.6s infinite ease-in-out;
      outline: 2px solid #f6ad55;
      outline-offset: -2px;
      box-shadow: 0 0 14px rgba(246, 173, 85, 0.55);
    }
    .seq-step.has-variance {
      position: relative;
    }
    .seq-step.has-variance::after {
      content: '⟳';
      position: absolute;
      top: 1px;
      right: 3px;
      font-size: 0.62rem;
      font-weight: 700;
      color: #f6ad55;
      pointer-events: none;
    }
    .seq-step.has-variance[data-variance-mode="linear"]::after {
      content: '↻';
    }

    /* SOUND EDITOR MODAL */
    .sm-overlay {
      position: fixed; inset: 0;
      background: rgba(0,0,0,0.72);
      z-index: 1100;
      display: flex; align-items: center; justify-content: center;
    }

    .sm-modal {
      background: #050510;
      border: 1px solid #2d2d3f;
      border-radius: 14px;
      padding: 18px 18px 16px;
      width: min(380px, 92vw);
      box-shadow: 0 16px 48px rgba(0,0,0,0.65), 0 0 28px rgba(0, 191, 255, 0.06);
    }
    /* Render-progress modal — status line above the bar describes the
       current phase (Building FX chain… / Decoding samples… /
       Rendering audio…), the percent + time-elapsed line below the
       bar shows the offline render's forward motion once it kicks
       in. Both update independently so a long setup phase still
       gives the user something to read. */
    .render-progress-modal .render-progress-status {
      color: #e2e8f0;
      font-family: 'Segoe UI', sans-serif;
      font-size: 0.85rem;
      font-weight: 600;
      text-align: center;
      margin: 6px 0 10px;
      min-height: 1.2em;
    }
    .render-progress-modal .render-progress-bar {
      height: 10px;
      background: #0a0a14;
      border: 1px solid #2d2d3f;
      border-radius: 6px;
      overflow: hidden;
    }
    .render-progress-modal .render-progress-fill {
      height: 100%;
      width: 0%;
      background: linear-gradient(90deg, #2b6cb0 0%, #38b2ac 60%, #81e6d9 100%);
      transition: width 160ms ease-out;
    }
    .render-progress-modal .render-progress-pct {
      color: #a0aec0;
      font-family: 'SF Mono', 'Menlo', monospace;
      font-size: 0.74rem;
      text-align: center;
      margin-top: 8px;
      min-height: 1.2em;
    }

    .sm-title {
      font-family: 'Segoe UI', sans-serif;
      font-size: 0.95rem; font-weight: 700;
      color: #f0f4ff; margin-bottom: 18px;
    }

    .sm-waves {
      display: flex; flex-wrap: wrap; gap: 6px; margin-bottom: 18px;
    }

    .sm-wave {
      padding: 4px 12px; border-radius: 14px;
      border: 1px solid #2d2d3f; background: transparent;
      color: #a0aec0; font-size: 0.73rem; font-weight: 600;
      font-family: 'Segoe UI', sans-serif;
      cursor: pointer; transition: all 0.15s;
    }
    @media (hover: hover) {
      .sm-wave:hover { border-color: #4299e1; color: #4299e1; }
    }
    .sm-wave.active { border-color: #4299e1; color: #4299e1; background: rgba(66,153,225,0.12); }
    .sm-wave--song { border-color: rgba(183, 148, 244, 0.45); color: #d6bcfa; }
    @media (hover: hover) {
      .sm-wave--song:hover { border-color: #b794f4; color: #e9d8fd; background: rgba(183, 148, 244, 0.12); }
    }

    .sm-param { margin-bottom: 12px; }
    .sm-param-row {
      display: flex; justify-content: space-between; align-items: baseline;
      font-family: 'Segoe UI', sans-serif; font-size: 0.73rem;
      color: #a0aec0; margin-bottom: 4px;
    }
    .sm-param-row .sm-val { color: #e2e8f0; font-weight: 600; }
    .sm-param input[type=range] { width: 100%; accent-color: #4299e1; }
    .sm-param .sm-select {
      width: 100%;
      background: #1a1a2e; color: #e2e8f0;
      border: 1px solid #2d2d3f; border-radius: 4px;
      padding: 4px 6px; font-size: 0.85rem;
      font-family: inherit;
    }
    .sm-param .sm-select:focus { outline: none; border-color: #4299e1; }
    .sm-fx-group {
      margin: 8px 0; padding: 6px 8px;
      background: #0f1020; border: 1px solid #2d2d3f; border-radius: 4px;
    }
    .sm-fx-group > summary {
      cursor: pointer; user-select: none;
      font-family: 'Segoe UI', sans-serif; font-size: 0.78rem; font-weight: 600;
      color: #a0aec0; padding: 2px 0;
    }
    .sm-fx-group[open] > summary { color: #e2e8f0; margin-bottom: 6px; }
    .sm-apply-all {
      display: inline-flex; align-items: center; gap: 6px;
      margin: 0 0 10px 0; padding: 3px 10px;
      background: rgba(79, 209, 197, 0.04);
      border: 1px solid #4fd1c5;
      border-radius: 14px;
      font-family: 'Segoe UI', sans-serif;
      font-size: 0.68rem; letter-spacing: 0.3px;
      color: #4fd1c5; font-weight: 600;
      cursor: pointer; user-select: none;
      text-shadow: 0 0 4px rgba(79, 209, 197, 0.35);
    }
    .sm-apply-all input { accent-color: #4fd1c5; cursor: pointer; }

    .sm-footer {
      display: flex; gap: 8px; margin-top: 20px; justify-content: flex-end;
    }

    .sm-preview {
      padding: 6px 14px; border-radius: 18px;
      background: transparent; border: 1px solid #2d2d3f;
      color: #a0aec0; font-family: 'Segoe UI', sans-serif;
      font-size: 0.78rem; cursor: pointer; transition: all 0.15s;
    }
    .sm-preview:hover { border-color: #4299e1; color: #4299e1; }

    .sm-apply {
      padding: 6px 18px; border-radius: 18px;
      background: linear-gradient(135deg, #2b6cb0, #4299e1);
      border: none; color: white;
      font-family: 'Segoe UI', sans-serif; font-size: 0.78rem;
      cursor: pointer; transition: all 0.15s;
    }
    .sm-apply:hover { transform: translateY(-1px); box-shadow: 0 4px 12px rgba(66,153,225,0.275); }

    /* Wrap-edit modal — narrower than the full step editor since each
       row holds a single label + one or two buttons. The contents
       re-render in place after every action (invert / toggle) so the
       summary line stays accurate without closing the menu. */
    .wrap-edit-modal { width: min(340px, 92vw); }
    .wrap-edit-modal .we-summary {
      font-family: 'Segoe UI', sans-serif;
      font-size: 0.82rem;
      color: #81e6d9;
      background: rgba(79, 209, 197, 0.08);
      border: 1px solid rgba(79, 209, 197, 0.25);
      border-radius: 8px;
      padding: 8px 10px;
      margin-bottom: 14px;
      word-break: break-word;
    }
    .wrap-edit-modal .we-row {
      display: flex;
      align-items: center;
      justify-content: space-between;
      gap: 10px;
      margin-bottom: 10px;
    }
    .wrap-edit-modal .we-row:last-of-type { margin-bottom: 0; }
    .wrap-edit-modal .we-label {
      font-family: 'Segoe UI', sans-serif;
      font-size: 0.78rem;
      font-weight: 600;
      color: #a0aec0;
      letter-spacing: 0.5px;
      flex: 0 0 auto;
    }
    .wrap-edit-modal .we-row-btns {
      display: flex;
      gap: 6px;
      flex: 1 1 auto;
      justify-content: flex-end;
    }
    .wrap-edit-modal .we-btn {
      padding: 6px 14px;
      border-radius: 18px;
      background: transparent;
      border: 1px solid #4fd1c5;
      color: #b2f5ea;
      font-family: 'Segoe UI', sans-serif;
      font-size: 0.78rem;
      font-weight: 600;
      cursor: pointer;
      transition: all 0.15s;
    }
    @media (hover: hover) {
      .wrap-edit-modal .we-btn:hover {
        background: rgba(79, 209, 197, 0.18);
        color: #e6fffa;
      }
    }
    .wrap-edit-modal .we-btn[disabled] {
      opacity: 0.35;
      cursor: not-allowed;
    }
    .wrap-edit-modal .we-toggle {
      min-width: 130px;
      text-align: center;
    }

    /* Wrap note-by-note editor — tabs at the top let the user pick which
       chord voice / subStep to edit; the editor body below shows that
       note's pitch + sound fields. */
    .wrap-notes-modal { width: min(420px, 94vw); }
    .wrap-notes-modal .wn-tabs {
      display: flex;
      flex-wrap: wrap;
      gap: 4px;
      margin-bottom: 14px;
      padding-bottom: 8px;
      border-bottom: 1px solid #2d2d3f;
    }
    .wrap-notes-modal .wn-tab {
      padding: 5px 12px;
      border-radius: 14px;
      border: 1px solid #2d2d3f;
      background: transparent;
      color: #a0aec0;
      font-family: 'Segoe UI', sans-serif;
      font-size: 0.75rem;
      font-weight: 600;
      cursor: pointer;
      transition: all 0.15s;
    }
    @media (hover: hover) {
      .wrap-notes-modal .wn-tab:hover { border-color: #4fd1c5; color: #b2f5ea; }
    }
    .wrap-notes-modal .wn-tab.active {
      border-color: #4fd1c5;
      color: #e6fffa;
      background: rgba(79, 209, 197, 0.18);
      box-shadow: 0 0 8px rgba(79, 209, 197, 0.35);
    }

    .rename-input {
      width: 100%;
      background: #0a0a14;
      border: 1px solid #2d2d3f;
      border-radius: 8px;
      color: #e2e8f0;
      font-size: 0.9rem;
      font-family: 'Segoe UI', sans-serif;
      padding: 8px 10px;
      margin-bottom: 12px;
    }
    .rename-input:focus { outline: none; border-color: #4299e1; }

    /* Modal scrolling — touch-action: pan-y tells iOS the content scrolls
       vertically so a finger drag inside the modal scrolls the modal rather
       than getting captured by the body. overscroll-behavior keeps scroll
       chains from leaking out into the page underneath. */
    .sm-modal {
      max-height: 85vh;
      overflow-y: auto;
      -webkit-overflow-scrolling: touch;
      overscroll-behavior: contain;
      touch-action: pan-y;
    }
    .sm-modal::-webkit-scrollbar { width: 6px; }
    .sm-modal::-webkit-scrollbar-track { background: transparent; }
    .sm-modal::-webkit-scrollbar-thumb { background: #2d2d3f; border-radius: 3px; }

    /* Collapsible "fold" sections — group related controls under a click-to-
       expand summary header, matching the menubar's panel aesthetic so the
       editor reads as a stack of compact dropdowns rather than a tall list. */
    .sm-fold {
      margin-bottom: 8px;
      border: 1px solid #2d2d3f;
      border-radius: 10px;
      background: #0a0a14;
      overflow: hidden;
    }
    .sm-fold[open] { border-color: #4299e1; }
    .sm-fold > summary {
      padding: 10px 14px;
      cursor: pointer;
      user-select: none;
      font-family: 'Segoe UI', sans-serif;
      font-size: 0.7rem;
      font-weight: 700;
      letter-spacing: 1px;
      text-transform: uppercase;
      color: #a0aec0;
      display: flex;
      justify-content: space-between;
      align-items: center;
      list-style: none;
    }
    .sm-fold > summary::-webkit-details-marker { display: none; }
    .sm-fold > summary::after {
      content: '▾';
      font-size: 0.7rem;
      color: #4a4a6a;
      transition: transform 0.15s ease;
    }
    .sm-fold[open] > summary { color: #e2e8f0; }
    .sm-fold[open] > summary::after { transform: rotate(180deg); color: #4299e1; }
    @media (hover: hover) {
      .sm-fold > summary:hover { color: #4299e1; }
      .sm-fold > summary:hover::after { color: #4299e1; }
    }
    .sm-fold-body {
      padding: 12px 14px 14px;
      border-top: 1px solid #2d2d3f;
    }
    .sm-fold-body .sm-waves { margin-bottom: 0; }
    .sm-fold-body .sm-param:last-child { margin-bottom: 0; }
    .sm-fold-body > .sm-section-label:first-child { margin-top: 0; }

    .sm-remove-step {
      padding: 6px 14px; border-radius: 18px;
      background: transparent; border: 1px solid #2d2d3f;
      color: #fc8181; font-family: 'Segoe UI', sans-serif;
      font-size: 0.78rem; cursor: pointer; transition: all 0.15s;
      margin-right: auto;
    }
    .sm-remove-step:hover { border-color: #fc8181; background: rgba(252,129,129,0.08); }

    .sm-section-label {
      font-family: 'Segoe UI', sans-serif; font-size: 0.7rem;
      font-weight: 700; letter-spacing: 1px; text-transform: uppercase;
      color: #4a4a6a; margin: 14px 0 8px;
    }
    .sm-checkbox-row {
      display: flex; align-items: center; gap: 8px;
      font-family: 'Segoe UI', sans-serif; font-size: 0.78rem;
      color: #a0aec0; cursor: pointer; user-select: none;
      padding: 4px 0;
    }
    .sm-checkbox-row input[type=checkbox] { cursor: pointer; }
    /* Black-filled checkboxes everywhere — accent-color paints the
       check-mark surface, so the box reads as filled black when ticked. */
    input[type=checkbox] { accent-color: #000; }

    .se-chord-list { margin-bottom: 4px; }
    .se-chord-row {
      display: flex; align-items: center; gap: 6px;
      padding: 7px 0; border-bottom: 1px solid #2d2d3f;
    }
    .se-chord-note-label {
      flex: 1; font-family: 'Segoe UI', sans-serif;
      font-size: 0.85rem; font-weight: 700; color: #e2e8f0;
      /* Now a clickable <button> — tapping opens the inline pitch
         picker so the user can retune this voice. Reset the default
         button chrome so it still reads as a label, with a subtle
         hover hint to flag the affordance. */
      background: none;
      border: none;
      padding: 0;
      text-align: left;
      cursor: pointer;
      transition: color 0.12s ease;
    }
    .se-chord-note-label:hover { color: #b3d4ff; }
    .se-wave-badge {
      font-size: 0.63rem; color: #a0aec0; font-family: 'Segoe UI', sans-serif;
      background: #0f0f1a; border: 1px solid #2d2d3f;
      border-radius: 4px; padding: 2px 5px;
    }
    .se-edit-btn {
      background: none; border: none; cursor: pointer;
      font-size: 0.72rem; font-family: 'Segoe UI', sans-serif;
      color: #4299e1; padding: 3px 7px; border-radius: 8px; transition: all 0.15s;
    }
    .se-edit-btn:hover { background: rgba(66,153,225,0.12); }
    .se-remove-btn {
      background: none; border: none; cursor: pointer;
      font-size: 0.95rem; color: #fc8181; padding: 3px 6px;
      border-radius: 8px; transition: all 0.15s; line-height: 1;
    }
    .se-remove-btn:hover { background: rgba(252,129,129,0.1); }
    .se-inline-sound {
      padding: 10px 4px 4px;
      border-bottom: 1px solid #2d2d3f;
      background: #0f0f1a; border-radius: 0 0 6px 6px;
      margin-bottom: 2px;
    }

    .sm-copy {
      padding: 6px 14px; border-radius: 18px;
      background: transparent; border: 1px solid #2d2d3f;
      color: #a0aec0; font-family: 'Segoe UI', sans-serif;
      font-size: 0.78rem; cursor: pointer; transition: all 0.15s;
      margin-right: auto;
    }
    .sm-copy:hover { border-color: #a0aec0; color: #e2e8f0; }

    /* Home bar — full-width link back to the mikeluz.com hero, branded
       "Mercy Wizard". Sits at the very top of the page above the Bloops /
       Player tabs. */
    /* Top sticky row: undo/redo on the left, Mercy Wizard centered, an
       invisible spacer on the right that mirrors the undo/redo width so
       the heading stays optically centered regardless of how wide the
       undo/redo cluster grows. */
    .home-bar-row {
      display: grid;
      grid-template-columns: 1fr auto 1fr;
      align-items: center;
      gap: 8px;
      background: #000;
      border-bottom: 1px solid #2d2d3f;
      padding: 4px 8px;
      position: sticky;
      top: 0;
      z-index: 100;
      /* Lock the row height so it doesn't shrink in Listen view (where
         undo/redo + BPM are display:none, leaving only the shorter
         Mercy Wizard text). 40px fits the BPM digit (24px) and the
         undo/redo button (~28px) plus the 4+4 vertical padding with a
         bit of headroom — same height across Make / Mix / Listen. */
      min-height: 40px;
    }
    .home-bar-row .undo-redo-pair {
      display: flex;
      justify-self: start;
      grid-column: 1;
    }
    /* BPM picker tucks into the right column of the home row. The full-
       size 3-digit picker (used elsewhere) is too tall for a compact
       header strip; scale down step buttons + digits so the row stays
       slim. Outer hidden inputs still drive every tempo* reference.
       Explicit grid-column: 3 because the home-bar is now absolutely
       positioned (removed from grid flow), and without an explicit
       column the auto-placement would put tempo-control into col 2. */
    .home-bar-row .tempo-control {
      justify-self: end;
      gap: 6px;
      grid-column: 3;
    }
    .home-bar-row .bpm-digits { gap: 3px; }
    .home-bar-row .bpm-digit {
      min-width: 22px;
      height: 24px;
      padding: 0 4px;
      font-size: 0.95rem;
      cursor: pointer;
    }
    .home-bar-row .bpm-digit:hover {
      border-color: #81e6d9;
      color: #b2f5ea;
      background: rgba(79, 209, 197, 0.12);
    }
    /* Listen view hides the entire bloops chrome except the home bar
       itself; the per-control hides below additionally drop undo/redo +
       BPM so the header is just "Mercy Wizard" while listening. */
    body.view-serialbox .home-bar-row .undo-redo-pair,
    body.view-serialbox .home-bar-row .tempo-control {
      display: none;
    }
    .home-bar {
      box-sizing: border-box;
      text-align: center;
      padding: 4px 12px;
      background: transparent;
      color: #4fd1c5;
      text-decoration: none;
      font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
      font-size: 0.95rem;
      font-weight: 700;
      letter-spacing: 1.5px;
      text-transform: uppercase;
      text-shadow: 0 0 10px rgba(79, 209, 197, 0.5);
      transition: color 0.15s ease, text-shadow 0.15s ease;
      /* Pin to the row's true horizontal center. The grid layout
         (1fr auto 1fr) used to put it in the auto middle column, but
         when the right (BPM) cluster grew wider than the left
         (undo/redo) the 1fr columns expanded asymmetrically and the
         middle drifted off page-center. Absolute centering is
         independent of the side columns. The grid still positions
         the side clusters; this just floats the title over the row. */
      position: absolute;
      left: 50%;
      top: 50%;
      transform: translate(-50%, -50%);
      pointer-events: auto;
      white-space: nowrap;
    }
    .home-bar:hover {
      color: #81e6d9;
      text-shadow: 0 0 14px rgba(129, 230, 217, 0.65);
    }

    /* Top bar — two tabs that switch between the Bloops sequencer and the
       Serialbox player. Left half doubles as the WIP / project-name banner;
       right half is a link to the Serialbox view. Sign-in is shared, so
       both share a single Google access token. */
    .top-bar {
      display: flex;
      align-items: stretch;
      width: 100%;
      flex-shrink: 0;
    }
    .top-bar > button {
      border: none;
      font: inherit;
      cursor: pointer;
    }
    /* Make / Mix / Listen tabs — typography matches the Mercy Wizard
       home-bar (same Segoe UI, 700 weight, 1.5px tracking, uppercase,
       neon teal glow) so the top headers read as one consistent banner
       stack. */
    .wip-banner,
    .mix-top-link,
    .serialbox-top-link {
      flex: 1 1 0;
      min-width: 0;
      box-sizing: border-box;
      text-align: center;
      padding: 8px 12px;
      background: #050510;
      color: #4fd1c5;
      text-decoration: none;
      font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
      font-size: 0.95rem;
      font-weight: 700;
      letter-spacing: 1.5px;
      text-transform: uppercase;
      text-shadow: 0 0 10px rgba(79, 209, 197, 0.5);
      display: flex;
      align-items: center;
      justify-content: center;
      transition: background 0.15s ease, color 0.15s ease, opacity 0.15s ease, text-shadow 0.15s ease;
    }
    .wip-banner { border-right: 1px solid #2d8478; }
    .mix-top-link { border-right: 1px solid #2d8478; }
    .serialbox-top-link { border-left: 1px solid #2d8478; }
    .wip-banner:not(.tab-active),
    .mix-top-link:not(.tab-active),
    .serialbox-top-link:not(.tab-active) { opacity: 0.55; }
    .wip-banner:not(.tab-active):hover,
    .mix-top-link:not(.tab-active):hover,
    .serialbox-top-link:not(.tab-active):hover {
      opacity: 0.85;
      background: #0a1416;
      color: #81e6d9;
      text-shadow: 0 0 14px rgba(129, 230, 217, 0.65);
    }
    .wip-banner.tab-active,
    .mix-top-link.tab-active,
    .serialbox-top-link.tab-active { background: #0a1416; }

    /* Listen tab uses the inverse of the shared palette — teal fill,
       dark text — so the Player tab visually pops as the "destination"
       half of the header. Overrides come after the shared rules above so
       they win on the cascade for hover, inactive, and active states. */
    .serialbox-top-link {
      background: #4fd1c5;
      color: #050510;
      text-shadow: none;
    }
    .serialbox-top-link:not(.tab-active) { opacity: 0.85; }
    .serialbox-top-link:not(.tab-active):hover {
      opacity: 1;
      background: #81e6d9;
      color: #050510;
      text-shadow: none;
    }
    .serialbox-top-link.tab-active {
      background: #81e6d9;
      color: #050510;
      box-shadow: inset 0 -2px 0 rgba(5, 5, 16, 0.4);
    }

    /* Sign-in gate — covers everything below the home bar until the user
       has signed in with Google. Both Bloops and the Player need Drive
       access, so we gate the whole page rather than each tab. */
    .signin-gate {
      display: none;
      position: fixed;
      inset: 0;
      background: #050510;
      /* Below the home-bar (z-index 100) so the "Mercy Wizard" link
         remains visible/clickable as an escape hatch back to index. */
      z-index: 50;
      align-items: center;
      justify-content: center;
      padding: 24px;
      box-sizing: border-box;
    }
    body.signin-required .signin-gate { display: flex; }
    body.signin-required > *:not(.signin-gate):not(.home-bar-row):not(script) {
      display: none !important;
    }
    .signin-gate-inner {
      max-width: 360px;
      text-align: center;
      color: #cbd5e0;
      font-family: 'Segoe UI', sans-serif;
    }
    .signin-gate-inner h2 {
      margin: 0 0 12px;
      font-size: 1.4rem;
      letter-spacing: 1px;
      color: #4fd1c5;
    }
    .signin-gate-inner p {
      margin: 0 0 24px;
      font-size: 0.95rem;
      color: #a0aec0;
    }
    #signin-btn {
      padding: 10px 22px;
      border: 1px solid #4fd1c5;
      border-radius: 22px;
      background: rgba(79, 209, 197, 0.10);
      color: #81e6d9;
      font-family: 'Segoe UI', sans-serif;
      font-size: 0.92rem;
      font-weight: 600;
      letter-spacing: 0.5px;
      cursor: pointer;
      transition: background 0.15s ease, color 0.15s ease, border-color 0.15s ease;
    }
    #signin-btn:hover {
      background: rgba(79, 209, 197, 0.22);
      border-color: #81e6d9;
      color: #b2f5ea;
    }
    #signin-btn:disabled { opacity: 0.55; cursor: progress; }

    /* Sample-loading gate — covers the Bloops UI until the default piano
       sampler has finished its network fetch, so the first cell tap can
       actually produce sound. Sits above the signin gate; hides itself
       once samples are ready. Not shown on the Player view (it doesn't
       need samples). */
    .bloops-loading-gate {
      display: none;
      position: fixed;
      inset: 0;
      background: #050510;
      z-index: 60;
      align-items: center;
      justify-content: center;
      padding: 24px;
      box-sizing: border-box;
    }
    body.bloops-loading:not(.signin-required):not(.view-serialbox) .bloops-loading-gate {
      display: flex;
    }
    .bloops-loading-inner {
      max-width: 360px;
      text-align: center;
      color: #cbd5e0;
      font-family: 'Segoe UI', sans-serif;
    }
    .bloops-loading-inner h2 {
      margin: 0 0 12px;
      font-size: 1.4rem;
      letter-spacing: 1px;
      color: #4fd1c5;
    }
    .bloops-loading-inner p {
      margin: 0;
      font-size: 0.95rem;
      color: #a0aec0;
    }
    .bloops-loading-dot {
      display: inline-block;
      width: 6px; height: 6px; margin: 0 2px;
      border-radius: 50%; background: #4fd1c5;
      animation: bloops-dot 1.2s ease-in-out infinite;
    }
    .bloops-loading-dot:nth-child(2) { animation-delay: 0.15s; }
    .bloops-loading-dot:nth-child(3) { animation-delay: 0.30s; }
    @keyframes bloops-dot {
      0%, 80%, 100% { opacity: 0.25; transform: scale(0.85); }
      40% { opacity: 1; transform: scale(1); }
    }

    /* View switching — exactly one of the three app views (Make, Mix,
       Listen) is visible at a time. Body class drives which set of
       top-level children show. */
    #serialbox-view { display: none; flex: 1 1 auto; min-height: 0; overflow-y: auto; padding: 16px; box-sizing: border-box; }
    body.view-serialbox > #serialbox-view { display: block; }
    body.view-serialbox > *:not(.home-bar-row):not(.top-bar):not(#serialbox-view):not(script) {
      display: none !important;
    }
    body.view-serialbox { overflow-y: auto; }
    /* Serialbox container override — drop the global .container max-width
       so the embedded view fills the available space inside the body. */
    #serialbox-view .container { max-width: 1100px; margin: 0 auto; padding: 0; }

    /* Mix view — wraps the Tracks section + Mixdown bar. Hidden by
       default so the Make view doesn't see it; revealed when the body
       carries .view-mix. State (tracks, mixdown buffers, sequences) is
       all module-scoped JS, so swapping views never resets data. */
    #mix-view { display: none; flex: 1 1 auto; min-height: 0; overflow-y: auto; box-sizing: border-box; }
    body.view-mix > #mix-view { display: block; }
    body.view-mix > *:not(.home-bar-row):not(.top-bar):not(#mix-view):not(.ctx-menu):not(.sm-overlay):not(.modal-overlay):not(.track-item-popover):not(script) {
      display: none !important;
    }
    body.view-mix { overflow-y: auto; }
    /* Hard lockdown: #mix-view is Mix-only. Beats the
       body.tracks-fullscreen > #mix-view { display: block !important }
       rule above when the user is on Make/Listen but tracks-fullscreen
       was left on the body. Without this, the Tracks section (and its
       Export button) leaked onto Make. */
    body:not(.view-mix) #mix-view { display: none !important; }
    .sm-copy.open  { border-color: #4299e1; color: #4299e1; }

    .sm-copy-panel {
      margin-top: 14px;
      padding-top: 14px;
      border-top: 1px solid #2d2d3f;
    }
    .sm-copy-label {
      font-family: 'Segoe UI', sans-serif;
      font-size: 0.73rem; color: #a0aec0;
      margin-bottom: 10px;
    }
    .sm-copy-notes {
      display: flex; flex-wrap: wrap; gap: 6px;
      margin-bottom: 12px;
    }
    .sm-copy-note {
      padding: 4px 10px; border-radius: 14px;
      border: 1px solid #2d2d3f; background: transparent;
      color: #a0aec0; font-size: 0.73rem; font-weight: 600;
      font-family: 'Segoe UI', sans-serif;
      cursor: pointer; transition: all 0.15s;
    }
    .sm-copy-note:hover   { border-color: #4299e1; color: #4299e1; }
    .sm-copy-note.picked  { border-color: #4299e1; color: #4299e1; background: rgba(66,153,225,0.15); }
    .sm-copy-actions { display: flex; gap: 8px; justify-content: flex-end; }
    .sm-copy-all {
      padding: 5px 14px; border-radius: 18px;
      background: transparent; border: 1px solid #2d2d3f;
      color: #a0aec0; font-family: 'Segoe UI', sans-serif;
      font-size: 0.75rem; cursor: pointer; transition: all 0.15s;
    }
    .sm-copy-all:hover { border-color: #e2e8f0; color: #e2e8f0; }
    .sm-copy-sel {
      padding: 5px 14px; border-radius: 18px;
      background: linear-gradient(135deg, #2b6cb0, #4299e1);
      border: none; color: white;
      font-family: 'Segoe UI', sans-serif; font-size: 0.75rem;
      cursor: pointer; transition: all 0.15s;
    }
    .sm-copy-sel:disabled { opacity: 0.35; cursor: not-allowed; }
    .sm-copy-sel:not(:disabled):hover { transform: translateY(-1px); box-shadow: 0 4px 12px rgba(66,153,225,0.275); }
