  :root {
    /* flexoki (light) — warm paper + ink, one moss accent */
    --paper:#FFFCF0; --bg:#F2F0E5; --card:#FFFCF0; --panel:#F7F2E6;
    --line:#DAD8CE; --line2:#CECDC3; --roll:#EDEADD;
    --ink:#100F0F; --ink2:#6F6E69; --ink3:#B7B5AC;
    --acc:#66800B; --acc-d:#556b09; --acc-ink:#FFFCF0;
    --a:#AD8301; --b:#24837B;  /* loop A / B markers (ochre / cyan) */
    --serif:"Charter","Iowan Old Style",Georgia,serif;
    --sans:system-ui,-apple-system,"Segoe UI",sans-serif;
  }
  * { box-sizing:border-box; }
  html,body { margin:0; min-height:100%; background:var(--bg); color:var(--ink); font-family:var(--sans); }
  body { display:flex; flex-direction:column; align-items:center; }
  .wrap { width:100%; max-width:940px; padding:22px 18px 46px; display:flex; flex-direction:column; gap:14px; }
  header { display:flex; justify-content:space-between; align-items:baseline; border-bottom:1px solid var(--line); padding-bottom:12px; }
  header h1 { font-family:var(--serif); font-size:22px; font-weight:600; margin:0; letter-spacing:-.01em; }
  header h1 .dot { color:var(--acc); }
  header h1 .beta { font:500 12px var(--sans); color:var(--ink3); vertical-align:middle; margin-left:2px; }
  header .sub { color:var(--ink2); font-size:13px; font-family:var(--serif); font-style:italic; }

  #drop { border:1.5px dashed var(--line2); border-radius:12px; padding:46px 24px; text-align:center;
    background:var(--paper); cursor:pointer; transition:.15s; }
  #drop.hot { border-color:var(--acc); background:rgba(102,128,11,.06); }
  #drop .big { font-family:var(--serif); font-size:21px; margin-bottom:8px; letter-spacing:-.01em; }
  #drop .small { color:var(--ink2); font-size:13.5px; line-height:1.7; }
  #drop input { display:none; }
  .qlink { color:var(--acc); font-size:13px; font-weight:600; text-decoration:none; cursor:pointer; }
  .qlink:hover { text-decoration:underline; }
  .qlink.muted { color:var(--ink2); font-weight:500; }

  #stage { display:none; flex-direction:column; gap:12px; }
  #planslist, #plandoc { display:none; flex-direction:column; gap:14px; }
  .topgrid { display:flex; gap:12px; align-items:stretch; }
  #videowrap { position:relative; width:50%; display:flex; }
  video { width:100%; border-radius:10px; background:#000; display:block; max-height:52vh; border:1px solid var(--line2); cursor:pointer; }
  .vcrop { position:absolute; right:10px; bottom:10px; width:34px; height:34px; padding:0; border-radius:8px; background:rgba(16,15,15,.45); border:none; color:#fff; display:grid; place-items:center; cursor:pointer; transition:background .15s; z-index:2; }
  .vcrop:hover { background:rgba(16,15,15,.65); } .vcrop.on { background:rgba(255,252,240,.94); color:var(--ink); }
  .vcrop svg { width:16px; height:16px; display:block; }
  /* pane-zoom toggles: expand the roll OR the video to fill the top area; the other shrinks to a PiP corner.
     The icon flips between "maximize" (expand this pane) and "minimize" (restore the split), so it always
     reflects what a click will do. Roll button sits on cream, video button on the dark video (like fit/fill). */
  .panezoom svg { width:16px; height:16px; display:block; }
  .panezoom .ico-min { display:none; }
  .panezoom.on .ico-max { display:none; } .panezoom.on .ico-min { display:block; }
  .rollzoom { position:absolute; right:8px; top:8px; width:34px; height:34px; padding:0; border-radius:8px; z-index:4;
    background:rgba(255,252,240,.82); border:1px solid var(--line2); color:var(--ink2); display:grid; place-items:center; cursor:pointer; transition:.15s; }
  .rollzoom:hover { color:var(--ink); border-color:var(--ink3); }
  .dlmidi { position:absolute; right:48px; top:8px; width:34px; height:34px; padding:0; border-radius:8px; z-index:4; background:rgba(255,252,240,.82); border:1px solid var(--line2); color:var(--ink2); display:grid; place-items:center; cursor:pointer; transition:color .15s, border-color .15s, background .15s; }
  .dlmidi:hover { color:var(--acc-d); border-color:var(--acc); background:rgba(102,128,11,.1); }
  .dlmidi svg { width:16px; height:16px; display:block; }
  .rollzoom.on { background:rgba(102,128,11,.16); border-color:var(--acc); color:var(--acc-d); }
  .vidzoom { position:absolute; left:10px; top:10px; width:34px; height:34px; padding:0; border-radius:8px; z-index:3;
    background:rgba(16,15,15,.45); border:none; color:#fff; display:grid; place-items:center; cursor:pointer; transition:background .15s; }
  .vidzoom:hover { background:rgba(16,15,15,.65); } .vidzoom.on { background:rgba(255,252,240,.94); color:var(--ink); }
  body.rollfocus .topgrid, body.vidfocus .topgrid { position:relative; }

  /* roll-focus: roll fills, video -> portrait PiP (sized by height so it stays compact + clears the button).
     body.playing.<mode> out-specifies the layout media queries that set #videowrap/#rollwrap height/width/flex. */
  body.playing.rollfocus #videowrap { position:absolute; z-index:6; right:10px; bottom:10px; height:42%; width:auto; aspect-ratio:9/16; flex:none;
    border-radius:10px; overflow:hidden; box-shadow:0 6px 20px -6px rgba(16,15,15,.55); max-height:none; }
  body.playing.rollfocus #videowrap video { height:100%; width:100%; max-height:none; object-fit:cover; }
  body.rollfocus .vcrop { display:none; }   /* no fit/fill control on the tiny PiP */

  /* video-focus: video grows to fill the height at its natural (portrait) width, centered (no black bars);
     roll -> a small landscape PiP strip in the corner. */
  body.playing.vidfocus .topgrid { justify-content:center; align-items:center; }
  body.playing.vidfocus #videowrap { width:auto; max-width:100%; height:100%; flex:none; }
  body.playing.vidfocus #videowrap video { height:100%; width:auto; max-height:none; object-fit:contain; }
  body.playing.vidfocus #rollwrap { position:absolute; z-index:6; right:10px; bottom:10px; width:46%; height:30%; flex:none; min-height:0;
    box-shadow:0 6px 20px -6px rgba(16,15,15,.4); }
  body.vidfocus .rollzoom { width:28px; height:28px; }   /* smaller control on the roll PiP */
  body.rollfocus .vidzoom { width:26px; height:26px; left:7px; top:7px; }

  /* Bottom-left corner (positioned by left/bottom, NOT a centering transform): the global
     `button:active{transform:translateY(1px)}` used to clobber a translate(-50%,-50%) centering,
     teleporting the button out from under the cursor on press so the click never landed. */
  .vplay { position:absolute; left:10px; bottom:10px; width:44px; height:44px; border-radius:50%; background:rgba(16,15,15,.55); border:none; display:grid; place-items:center; cursor:pointer; transition:background .15s; z-index:3; box-shadow:0 0 12px 2px rgba(0,0,0,.35); }
  .vplay:hover { background:rgba(16,15,15,.75); }
  .vplay .tri { width:0; height:0; border-style:solid; border-width:8px 0 8px 13px; border-color:transparent transparent transparent #fff; margin-left:3px; }
  .vplay.hidden { display:none; }
  #rollwrap { flex:1; background:var(--roll); border:1px solid var(--line2); border-radius:10px; position:relative; overflow:hidden; }
  #roll { width:100%; height:100%; display:block; cursor:grab; touch-action:none; }
  #rollwrap .empty { position:absolute; inset:0; display:flex; align-items:center; justify-content:center;
    color:var(--ink2); font-size:13px; text-align:center; padding:20px; }
  #refining { position:absolute; top:8px; right:8px; display:none; align-items:center; gap:6px;
    font-size:11px; color:var(--ink2); background:rgba(237,234,221,.85); border:1px solid var(--line2);
    border-radius:999px; padding:3px 9px; letter-spacing:.02em; }
  #refining.on { display:inline-flex; }
  #refining .dot { width:6px; height:6px; border-radius:50%; background:var(--acc); animation:pulse 1.1s ease-in-out infinite; }
  @keyframes pulse { 0%,100%{opacity:.35;} 50%{opacity:1;} }
  /* one-time coach-mark: teaches "the roll drags left/right". Shows on the first clip that has notes,
     leaves the instant the user drags. Centering lives in the keyframes (fill:both holds the end state),
     so there's no static transform to fight the entrance/exit animation. */
  #draghint { position:absolute; left:50%; bottom:16px; z-index:6; display:none; align-items:center;
    gap:.55em; white-space:nowrap; max-width:92%; pointer-events:none; transform:translateX(-50%);
    font:600 13.5px var(--sans); color:var(--ink); background:rgba(255,252,240,.95);
    border:1px solid var(--line2); border-radius:999px; padding:.6em 1.1em;
    box-shadow:0 4px 16px -4px rgba(60,50,30,.28); }   /* bottom of the roll, out of the notes */
  #draghint.on  { display:inline-flex; animation:draghintIn .32s cubic-bezier(.2,.8,.2,1) both; }
  #draghint.out { display:inline-flex; animation:draghintOut .24s ease-in both; }
  #draghint .arw { color:var(--acc-d); letter-spacing:-.12em; opacity:.9; }
  @keyframes draghintIn  { from{opacity:0; transform:translate(-50%,6px)} to{opacity:1; transform:translate(-50%,0)} }
  @keyframes draghintOut { from{opacity:1; transform:translate(-50%,0)} to{opacity:0; transform:translate(-50%,6px)} }
  @media (prefers-reduced-motion:reduce){ #draghint.on,#draghint.out{ animation-duration:.01ms; } }
  #keyswrap { height:78px; }
  #keys { width:100%; height:100%; display:block; background:var(--line2); border:1px solid var(--line2); border-radius:8px; }

  .bar { position:relative; height:8px; background:var(--line); border-radius:999px; cursor:pointer; margin:5px 0 0; touch-action:none; }
  .bar .played { position:absolute; top:0; left:0; height:100%; background:var(--acc); border-radius:999px; }
  .bar .loopfill { position:absolute; top:0; height:100%; background:rgba(102,128,11,.18); border-radius:2px; }
  .bar .marker { position:absolute; top:-7px; height:calc(100% + 14px); width:28px; cursor:ew-resize; touch-action:none; z-index:3; display:flex; align-items:center; }   /* 28px hit box (drag hit-test = this element); the slim pennant sits at the boundary edge inside it */
  .bar .marker::before, .bar .marker::after { content:''; display:block; }
  .bar .marker::after { display:none; }
  .bar .mA { transform:translateX(-18px); }                                                                       /* pad extends OUTWARD (left); pads must never overlap each other on a tight region */
  .bar .mA::before { width:100%; height:100%; background:url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 10 22"><path d="M1.25 0 A1.25 1.25 0 0 1 2.5 1.25 L2.5 6.5 L8.6 10.1 A1.4 1.4 0 0 1 8.6 11.9 L2.5 15.5 L2.5 20.75 A1.25 1.25 0 0 1 0 20.75 L0 1.25 A1.25 1.25 0 0 1 1.25 0 Z" fill="%23100F0F"/></svg>') no-repeat right center/auto 100%; }   /* |> slim pennant, post ON the boundary */
  .bar .mB { transform:translateX(-10px); }                                                                       /* pennant [b-10,b], pad extends OUTWARD (right) */
  .bar .mB::before { width:100%; height:100%; background:url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 10 22"><path d="M8.75 0 A1.25 1.25 0 0 0 7.5 1.25 L7.5 6.5 L1.4 10.1 A1.4 1.4 0 0 0 1.4 11.9 L7.5 15.5 L7.5 20.75 A1.25 1.25 0 0 0 10 20.75 L10 1.25 A1.25 1.25 0 0 0 8.75 0 Z" fill="%23100F0F"/></svg>') no-repeat left center/auto 100%; }   /* <| slim pennant, post ON the boundary */
  .bar .thumb { position:absolute; top:50%; left:0; width:15px; height:15px; border-radius:50%; background:var(--paper);
    transform:translate(-50%,-50%); box-shadow:0 1px 4px rgba(60,50,30,.35); border:1px solid var(--line2); pointer-events:none; }
  .times { display:flex; justify-content:space-between; color:var(--ink2); font-size:12px; font-variant-numeric:tabular-nums; margin-top:5px; }  /* clears the 15px thumb that overhangs the 8px bar */
  /* loop lives ON the scrubber line now (one toggle: tap to loop a section, tap to clear); replaces the old
     Hold/Loop/Clear group in the transport. A/B handles on the bar still drag to refine the region. */
  .barrow { display:flex; align-items:center; gap:12px; flex-wrap:wrap; row-gap:8px; }
  .barcol { flex:1; min-width:0; }
  .looptoggle { flex:none; display:inline-flex; align-items:center; gap:6px; font:600 12.5px var(--sans); color:var(--ink2);
    background:var(--card); border:1px solid var(--line2); border-radius:8px; padding:7px 11px; cursor:pointer; transition:.12s; }
  .looptoggle:hover { border-color:var(--ink3); color:var(--ink); }
  .looptoggle.on { background:rgba(102,128,11,.14); border-color:var(--acc); color:var(--acc-d); }
  .looptoggle { position:relative; }
  /* loop length presets: visible only while a loop is active; Custom lights when the handles were
     dragged to a non-preset size and re-applies that size when tapped */
  .lenpills { display:inline-flex; border:1px solid var(--line2); border-radius:8px; overflow:hidden; flex:none; }
  .lenpills button { font:600 11.5px var(--sans); color:var(--ink2); background:var(--card); border:0; padding:7px 9px; cursor:pointer; }
  .lenpills button + button { border-left:1px solid var(--line); }
  .lenpills button.on { background:rgba(102,128,11,.14); color:var(--acc-d); }
  .lenpills .leninwrap { display:inline-flex; align-items:center; gap:1px; border-left:1px solid var(--line); padding:0 8px 0 6px; }
  .lenpills .leninwrap.on { background:rgba(102,128,11,.14); }
  .lenpills .leninwrap.on input, .lenpills .leninwrap.on .lenunit { color:var(--acc-d); }
  .lenpills .leninwrap input { width:26px; border:0; background:transparent; font:600 11.5px var(--sans); color:var(--ink2); outline:none; text-align:right; font-variant-numeric:tabular-nums; }
  .lenpills .leninwrap input::-webkit-outer-spin-button, .lenpills .leninwrap input::-webkit-inner-spin-button { -webkit-appearance:none; margin:0; }
  .lenpills .lenunit { font:600 10.5px var(--sans); color:var(--ink2); opacity:.8; }
  .looptoggle .once1 { display:none; position:absolute; top:3px; left:19px; font:700 8px var(--sans); color:var(--acc-d);
    background:var(--card); border-radius:3px; padding:0 1.5px; line-height:1.15; }
  .looptoggle.once .once1 { display:block; }
  .looptoggle svg { width:14px; height:14px; display:block; }

  button { font-family:var(--sans); font-size:14px; color:var(--ink2); background:var(--card);
    border:1px solid var(--line2); border-radius:8px; padding:9px 13px; cursor:pointer; transition:.12s; }
  button:hover { border-color:var(--ink3); } button:active { transform:translateY(1px); }
  button.on { background:rgba(102,128,11,.13); border-color:var(--acc); color:var(--acc-d); font-weight:600; }
  /* compact transport: play + grouped segmented controls + loop group */
  .transport { display:flex; align-items:center; gap:12px 16px; flex-wrap:wrap; }
  .trow { display:contents; }   /* transparent on desktop (one row); becomes a flex row only in portrait (see below) */
  .pp { width:46px; height:46px; border-radius:50%; background:var(--acc); border:1px solid var(--acc-d); display:grid; place-items:center; cursor:pointer; padding:0; flex:none; }
  .pp:hover { background:var(--acc-d); border-color:var(--acc-d); }
  .pp .ico { width:0; height:0; border-style:solid; border-width:8px 0 8px 13px; border-color:transparent transparent transparent var(--acc-ink); margin-left:3px; }
  .pp.playing .ico { width:11px; height:15px; border:0; margin:0;   /* element = full glyph, so grid centering is true (bar+box-shadow centered only the first bar) */
    background:linear-gradient(90deg, var(--acc-ink) 0 4px, transparent 4px 7px, var(--acc-ink) 7px 11px); }
  .grp { display:flex; flex-direction:column; gap:5px; }
  .grp .cap { font:650 10px var(--sans); text-transform:uppercase; letter-spacing:.08em; color:var(--ink3); }
  .seg { display:inline-flex; background:var(--panel); border:1px solid var(--line2); border-radius:8px; padding:3px; }
  .seg button { border:0; background:none; padding:7px 12px; border-radius:6px; }
  .seg button:hover { color:var(--ink); }
  .seg button.on { background:rgba(102,128,11,.14); color:var(--acc-d); font-weight:600; }
  /* Key stepper reuses the plain .seg/.grp idiom (like Speed/Audio); the readout span must match .seg button type (14px, not the inherited 16px) */
  #tval { display:inline-flex; align-items:center; justify-content:center; min-width:3.9em; padding:0 8px; font:14px var(--sans); color:var(--ink2); font-variant-numeric:tabular-nums; cursor:pointer; user-select:none; }
  .loop { display:inline-flex; gap:6px; }
  .loop button { padding:7px 11px; font-size:12.5px; }
  .loop .setA.armed { border-color:var(--a); color:var(--a); }
  .loop .setB.armed { border-color:var(--b); color:var(--b); }
  /* ---- Playback settings: Audio + Key + Note length flow inline in the transport on desktop, collapse into a bottom sheet on mobile ---- */
  .settings { display:contents; }                          /* desktop: children flow inline in the transport row */
  .settings .grab, .settings .sheethd { display:none; }    /* sheet chrome exists only on mobile */
  .more { display:none; }                                  /* settings button appears only when collapsed (mobile) */
  .more svg { width:19px; height:19px; }
  .sheet-backdrop { display:none; }
  @media (max-width:640px){
    .more { display:inline-flex; align-items:center; justify-content:center; margin-left:auto; flex:none;
      width:40px; height:40px; padding:0; border-radius:8px; background:var(--card); border:1px solid var(--line2); color:var(--ink2); cursor:pointer; }
    .more:hover { color:var(--ink); border-color:var(--ink3); }
    .more[aria-expanded="true"] { background:rgba(102,128,11,.14); border-color:var(--acc); color:var(--acc-d); }
    .sheet-backdrop { display:block; position:fixed; inset:0; z-index:80; background:rgba(16,15,15,.38); opacity:0; pointer-events:none; transition:opacity .2s; }
    .sheet-backdrop.open { opacity:1; pointer-events:auto; }
    .settings { display:block; position:fixed; left:0; right:0; bottom:0; z-index:81; background:var(--paper);
      border-radius:16px 16px 0 0; padding:8px 18px calc(20px + env(safe-area-inset-bottom));
      box-shadow:0 -10px 34px rgba(60,50,30,.20); transform:translateY(101%); transition:transform .26s cubic-bezier(.2,.8,.2,1); }
    .settings.open { transform:translateY(0); }
    .settings .grab { display:block; width:38px; height:4px; border-radius:2px; background:var(--line2); margin:6px auto 12px; }
    .settings .sheethd { display:flex; align-items:center; justify-content:space-between; margin-bottom:2px; }
    .settings .sheethd .ttl { font-family:var(--serif); font-size:17px; font-weight:600; letter-spacing:-.01em; }
    .settings .sheethd .done { font:600 14px var(--sans); color:var(--acc-d); background:none; border:0; padding:6px 2px; cursor:pointer; }
    .settings .grp { flex-direction:row; align-items:center; justify-content:space-between; gap:16px; padding:15px 0; border-top:1px solid var(--line); }
    #settings .grp > .cap { display:block; font:600 14px var(--sans); color:var(--ink); text-transform:none; letter-spacing:normal; }   /* reuse the cap as the sheet row label (id beats the max-width:939px cap-hide) */
    .settings #notelenrow { flex-direction:column; align-items:stretch; gap:14px; }   /* note length: label above, full-width slider */
    .settings #notelenrow .lenwrap { width:100%; gap:9px; }
    .settings #notelenrow .lenrng { width:100%; }
  }
  /* Loop floats right ONLY when the whole transport fits one row (~940px+). Below that it would wrap and
     the right-float would strand it alone on row 2 with a gap, so we left-align and let it wrap cleanly. */
  .spread { margin-left:0; }
  @media (min-width:940px) { .transport .spread { margin-left:auto; } }
  /* vertical / narrow: caption labels stack ABOVE each control, so a wrapped transport carries a label line
     per row and balloons vertically (the complaint). Hide them below the one-row width — the controls read on
     their own: the highlighted 1x chip, Full mix / Piano only, the slider's shorter/longer ends, Hold/Loop/Clear. */
  @media (max-width:939px){
    .transport { gap:8px 14px; row-gap:8px; }
    .transport .grp > .cap { display:none; }
    #notelenrow .lenscale { display:none; }   /* with the NOTE LENGTH cap gone the words float context-free and collide with the detent notches when the slider wraps to its own row */
    #draghint { font-size:12.5px; }
  }
  .back { align-self:flex-start; background:none; border:none; color:var(--ink2); font:600 14px var(--sans); cursor:pointer; padding:2px 2px 2px 0; margin-bottom:2px; }
  .back:hover { color:var(--acc); }
  .navrow { display:flex; align-items:baseline; justify-content:space-between; gap:12px; }
  .navclip { display:none; }   /* only surfaces in landscape, where the header (and its clip name) is hidden */
  .clipby { align-self:center; align-items:center; gap:7px; text-decoration:none; color:var(--ink2); font:600 13px var(--sans); padding:4px 11px 4px 4px; border-radius:999px; border:1px solid var(--line2); background:var(--card); transition:border-color .12s, color .12s; flex:none; max-width:62%; }
  .clipby:hover { border-color:var(--acc); color:var(--acc-d); }
  .clipby .byav { width:24px; height:24px; border-radius:50%; background:#141210 center/cover no-repeat; flex:none; }
  .clipby .byname { white-space:nowrap; overflow:hidden; text-overflow:ellipsis; }
  .hint { color:var(--ink3); font-size:12px; line-height:1.7; }
  kbd { background:var(--panel); border:1px solid var(--line2); border-radius:5px; padding:1px 5px; font-size:11px; font-family:ui-monospace,monospace; color:var(--ink2); }

  #home { display:flex; flex-direction:column; gap:14px; }
  /* Deep-link opens straight to a clip — never paint the library first (set at parse time in index.html <head>). */
  html.deeplink #home { display:none; }
  /* deep-link cold-load: keep the wrap at the player width (no 940->600 profile flash), and show the REAL
     player shell (no separate skeleton) with the video/roll reserved + a subtle loading tint, so the screen
     never pops to a different SIZE when the clip fills in — the chrome is the actual same-styled elements. */
  html.deeplink body.profile-view:not(.playing) .wrap { max-width:940px; }
  html.deeplink #stage { display:flex; }
  html.deeplink body:not(.playing) .hint { display:none; }   /* the playing layout drops it too — reserving its space would shift every row on load */
  html.deeplink body:not(.playing) #videowrap video { max-height:none; }   /* like the playing state — else the 52vh cap shrinks the pane and the topgrid can't fill */
  html.deeplink body:not(.playing) #keyswrap { position:relative; overflow:hidden; border-radius:8px; }
  /* per-pane loading: each pane keeps its reserved box + shimmer until ITS media is ready — video: first
     frame (loadeddata/error); roll: MIDI fetched. index.html ships both panes with .loading so the pre-JS
     skeleton is the same treatment; quick() re-arms it per open and reveals each side independently. */
  #videowrap.loading { background:var(--roll); border:1px solid var(--line2); border-radius:10px; }   /* height comes from the full-height flex layout, not an aspect guess */
  #videowrap.loading > * { visibility:hidden; }   /* the empty <video> + its buttons — the pane itself is the placeholder */
  #videowrap.loading::before { content:''; position:absolute; left:50%; top:50%; width:28px; height:28px; margin:-14px 0 0 -14px;
    border:3px solid var(--line2); border-top-color:var(--acc); border-radius:50%; animation:sp 1s linear infinite; z-index:1; }   /* video is the slow one — say so */
  #rollwrap.loading > :not(#roll) { visibility:hidden; }   /* the "No MIDI" empty-state + zoom/dl buttons (roll canvas is transparent -> just the tinted pane shows) */
  #videowrap.loading::after, #rollwrap.loading::after,
  html.deeplink body:not(.playing) #keyswrap::after {
    content:''; position:absolute; inset:0; border-radius:10px; transform:translateX(-100%); pointer-events:none;
    background:linear-gradient(100deg, transparent 20%, rgba(255,252,240,.16) 50%, transparent 80%); animation:skShimmer 2s ease-in-out infinite; }
  @keyframes skShimmer { to { transform:translateX(100%); } }
  #drop .big { font-family:var(--serif); }
  #cards { display:block; }
  .shelfhead { font:650 12px var(--sans); text-transform:uppercase; letter-spacing:.07em; color:var(--ink2); margin:20px 0 12px; display:flex; gap:7px; align-items:center; }
  .shelfhead .n { color:var(--ink3); font-weight:600; }
  .arch-toggle { margin-left:auto; font-weight:600; color:var(--ink3); cursor:pointer; text-transform:none; letter-spacing:0; font-size:12px; }
  .arch-toggle:hover { color:var(--ink2); }
  /* uniform-cell grid (TikTok-style): one "mostly vertical" cell (--card-ar) cover-crops every clip, so
     rows are even and titles line up. Tune the shape with --card-ar. */
  /* align-items:start -> cards take natural height; the fixed 2-line title slot (min-height on .tt) makes
     that height identical for every card, so nothing is taller and the meta always lands at the bottom. */
  .grid { display:grid; grid-template-columns:repeat(auto-fill,minmax(150px,1fr)); gap:18px 14px; align-items:start; --card-ar:3/4; }
  .card { position:relative; background:var(--card); border:1px solid var(--line); border-radius:14px; overflow:hidden; cursor:pointer; transition:box-shadow .12s, transform .12s; box-shadow:0 1px 2px rgba(60,50,30,.05); }
  .card:hover { box-shadow:0 10px 24px rgba(60,50,30,.14); transform:translateY(-2px); }
  .card .poster { position:relative; aspect-ratio:var(--card-ar,3/4); background:var(--roll) center 42%/cover no-repeat; display:block; }
  .card .poster .dur { position:absolute; right:7px; bottom:7px; font:600 11px var(--sans); color:#fff; background:rgba(16,15,15,.6); padding:3px 7px; border-radius:6px; backdrop-filter:blur(3px); }
  /* waveform placeholder for clips with no usable frame (stubs / future audio-only) — reads as audio, not black */
  .card .poster .wave { position:absolute; inset:0; width:100%; height:100%; }
  .card .poster .wave rect { fill:var(--ink3); }
  /* absolute-fill the video so a real clip's intrinsic size can't override the poster's aspect-ratio, and
     so a loaded frame covers the waveform. (fallback for curated/pre-thumb clips crops to the same 3:4 cell.) */
  .card .poster video { position:absolute; inset:0; width:100%; height:100%; object-fit:cover; display:block; }
  .card .body { padding:9px 11px 10px; }
  .card .tt { font-family:var(--serif); font-size:15px; line-height:1.25; letter-spacing:-.005em; display:-webkit-box; -webkit-line-clamp:2; -webkit-box-orient:vertical; overflow:hidden; min-height:2.5em; }
  .card .ttedit { font-family:var(--serif); font-size:15px; width:100%; border:0; border-bottom:1.5px solid var(--acc); background:none; color:var(--ink); padding:1px 0; outline:none; }
  .card .meta { display:flex; align-items:center; justify-content:flex-end; gap:4px; margin-top:7px; font-size:12.5px; color:var(--ink2); }
  .card .menu-btn { width:24px; height:24px; border-radius:6px; border:0; cursor:pointer; background:none; color:var(--ink3); font-size:17px; line-height:1; display:grid; place-items:center; padding:0; }
  .card .menu-btn:hover { color:var(--ink); background:var(--panel); }
  .card .star { cursor:pointer; color:var(--ink3); font-size:15px; line-height:1; }
  .card .star.on { color:var(--acc); }
  .spin { width:20px; height:20px; border:2.5px solid var(--line2); border-top-color:var(--acc); border-radius:50%; animation:sp 1s linear infinite; }
  @keyframes sp { to { transform:rotate(360deg); } }
  /* background-upload job cards reuse the poster box */
  .card.jobwork .poster.jstate, .card.jobfail .poster.jstate { display:grid; place-items:center; background:var(--roll); }
  .card.jobwork .shim { position:absolute; inset:0; background:linear-gradient(110deg,transparent 30%,rgba(255,252,240,.10) 50%,transparent 70%); background-size:220% 100%; animation:shim 1.7s linear infinite; }
  @keyframes shim { to { background-position:-220% 0; } }
  .card .jlabel { font-size:12px; color:var(--acc-d); margin:8px 0 1px; display:flex; align-items:center; gap:6px; }
  .card .jlabel.err { color:var(--a); }
  .card .jlabel .jp { width:6px; height:6px; border-radius:50%; background:var(--acc); animation:jpulse 1.1s ease-in-out infinite; flex:none; }
  @keyframes jpulse { 0%,100%{opacity:.35;transform:scale(.8)} 50%{opacity:1;transform:scale(1)} }
  .card.jobfail .jact { display:flex; gap:6px; margin-top:8px; }
  .card.jobfail .jact button { flex:1; font-size:12px; padding:5px 8px; border-radius:6px; border:1px solid var(--acc); background:rgba(102,128,11,.1); color:var(--acc-d); cursor:pointer; font-weight:600; }
  .card.jobfail .jact button.ghost { border-color:var(--line2); background:none; color:var(--ink2); font-weight:500; }
  .card.jobfail .jx { width:26px; height:26px; border-radius:7px; display:grid; place-items:center; background:rgba(173,131,1,.14); color:var(--a); font-weight:700; }
  /* ⋯ row menu */
  .rowmenu { position:absolute; z-index:60; background:var(--card); border:1px solid var(--line2); border-radius:11px; box-shadow:0 12px 30px rgba(16,15,15,.2); padding:5px; min-width:150px; }
  .rowmenu button { display:block; width:100%; text-align:left; border:0; background:none; padding:9px 11px; border-radius:7px; cursor:pointer; color:var(--ink); font-size:14px; }
  .rowmenu button:hover { background:var(--panel); }
  .rowmenu button.danger { color:#9a3b28; }
  .rollwork { text-align:center; max-width:78%; }
  .rollwork .rwdots { display:flex; gap:5px; justify-content:center; margin-bottom:14px; }
  .rollwork .rwdots i { width:6px; height:6px; border-radius:50%; background:var(--acc); animation:rwd 1.2s ease-in-out infinite; }
  .rollwork .rwdots i:nth-child(2){ animation-delay:.16s } .rollwork .rwdots i:nth-child(3){ animation-delay:.32s }
  @keyframes rwd { 0%,100%{opacity:.25;transform:translateY(0)} 40%{opacity:1;transform:translateY(-3px)} }
  .rollwork .rwt { font-family:var(--serif); font-weight:600; font-size:17px; color:var(--ink); margin-bottom:6px; letter-spacing:-.01em; }
  .rollwork .rws { color:var(--ink2); font-size:12.5px; line-height:1.55; }
  #toast { position:fixed; left:50%; bottom:24px; transform:translate(-50%,14px); background:var(--ink); color:var(--paper); font-size:13.5px; padding:11px 16px; border-radius:10px; max-width:86vw; box-shadow:0 8px 24px rgba(16,15,15,.22); opacity:0; pointer-events:none; transition:opacity .2s, transform .2s; z-index:50; }
  #toast.on { opacity:1; transform:translate(-50%,0); }
  /* workspace: the "your space" chip + the gated-root landing */
  #wschip { align-items:center; gap:9px; font-size:12.5px; color:var(--ink2); background:var(--panel); border:1px solid var(--line2); border-radius:999px; padding:5px 6px 5px 13px; width:fit-content; margin:0 0 15px; }
  #wschip .wsdot { width:6px; height:6px; border-radius:50%; background:var(--acc); }
  #wschip .wstxt { font-weight:600; color:var(--ink); }
  #wschip button { font-size:12px; padding:4px 11px; border-radius:999px; border:1px solid var(--acc); background:rgba(102,128,11,.1); color:var(--acc-d); cursor:pointer; font-weight:600; }
  #gate { text-align:center; padding:64px 20px; max-width:520px; margin:0 auto; }
  #gate .gbig { font-family:var(--serif); font-size:25px; letter-spacing:-.01em; margin-bottom:11px; }
  #gate .gsmall { color:var(--ink2); font-size:14px; line-height:1.6; margin-bottom:24px; }
  #gate #gnew { font-family:var(--sans); font-size:14px; font-weight:600; padding:11px 22px; border-radius:10px; border:1px solid var(--acc-d); background:var(--acc); color:var(--acc-ink); cursor:pointer; }
  #gate #gnew:hover { background:var(--acc-d); }
  /* creator showcase: editorial profile header + demo disclaimer */
  #profile .phead { display:flex; gap:16px; align-items:flex-start; }
  #profile .pavatar { flex:none; width:84px; height:84px; border-radius:16px; background:#141210 center/cover no-repeat; box-shadow:0 2px 10px rgba(60,50,30,.15); }
  #profile .pmeta { min-width:0; padding-top:2px; }
  #profile .pname { font-family:var(--serif); font-size:25px; font-weight:600; letter-spacing:-.012em; line-height:1.05; }
  #profile .phandle { color:var(--acc-d); font-weight:600; font-size:13.5px; margin-top:3px; }
  #profile .pbio { color:var(--ink); font-size:14px; line-height:1.5; margin-top:11px; max-width:46ch; }
  #profile .plinks { display:flex; flex-direction:column; gap:7px; margin-top:16px; }
  #profile .plink { display:flex; align-items:center; justify-content:space-between; gap:8px; text-decoration:none; color:var(--ink); background:var(--card); border:1px solid var(--line2); border-radius:10px; padding:11px 14px; font-size:13.5px; font-weight:500; transition:border-color .12s, background .12s; }
  #profile .plink:hover { border-color:var(--acc); background:rgba(102,128,11,.06); }
  #profile .plink .arr { color:var(--ink3); font-size:13px; flex:none; }
  #profile .plink:hover .arr { color:var(--acc-d); }
  #profile .psocials { display:flex; gap:9px; margin-top:15px; }
  #profile .psocial { display:grid; place-items:center; width:38px; height:38px; border-radius:10px; background:var(--card); border:1px solid var(--line2); color:var(--ink2); transition:border-color .12s, color .12s, background .12s; }
  #profile .psocial:hover { border-color:var(--acc); color:var(--acc-d); background:rgba(102,128,11,.06); }
  #profile .psocial svg { width:18px; height:18px; fill:currentColor; }
  /* profile is a read-column, not the full library grid width -> narrow the wrap on wide screens */
  body.profile-view:not(.playing) .wrap { max-width:600px; }   /* narrow the PROFILE reading column only; player (body.playing) keeps full desktop width */
  #profile .pdisc { display:flex; gap:11px; align-items:flex-start; background:rgba(173,131,1,.1); border:1px solid rgba(173,131,1,.35); border-radius:11px; padding:12px 14px; margin:18px 0 2px; font-size:13px; line-height:1.5; color:#6b5209; }
  #profile .pdisc .ic { flex:none; width:20px; height:20px; border-radius:50%; background:var(--a); color:#fff; display:grid; place-items:center; font-weight:800; font-size:13px; margin-top:1px; }
  #profile .pdisc b { color:#4d3b06; }
  @media (max-width:640px) and (min-height:501px) and (orientation: portrait){
    body.waterfall #keyswrap{ height:54px; }
    .topgrid{ flex-direction:column; } #videowrap{ width:100%; } #rollwrap{ height:180px; }
    /* portrait: split the smashed 88-key row into two offset halves (treble up full, bass tucked below-left), uniform key size */
    #keyswrap{ height:58px; }   /* one clipped single row (the split two-row keyboard is gone) */

    /* vertical player: fit one screen — video + keyboard are the stars, the roll is a thin scrub strip.
       scoped to .playing so the library keeps scrolling normally. */
    body.playing{ overflow:hidden; }
    body.playing .wrap{ height:100dvh; gap:6px;
      padding:max(8px,env(safe-area-inset-top)) max(12px,env(safe-area-inset-right)) max(8px,env(safe-area-inset-bottom)) max(12px,env(safe-area-inset-left)); }
    body.playing header{ display:none; }   /* in the player you're on the video, not the app name — reclaim the whole row */
    body.playing #stage{ flex:1; min-height:0; gap:5px; }
    body.playing .navrow{ flex:none; align-items:center; } body.playing .back{ margin:0; }
    body.playing .navclip{ display:block; min-width:0; font:italic 12.5px var(--serif); color:var(--ink2); white-space:nowrap; overflow:hidden; text-overflow:ellipsis; }
    body.playing .topgrid{ flex:1; min-height:0; gap:6px; }
    body.playing #videowrap{ flex:1; min-height:0; }
    body.playing video{ max-height:none; height:100%; object-fit:contain; }
    body.playing #rollwrap{ height:110px; flex:none; }        /* scrub strip (you scrub to a chord here, read it on the keys) */
    body.playing.rollfocus #rollwrap{ flex:1; height:auto; }  /* roll-focus: the thin strip becomes the star */
    body.playing.rollfocus #videowrap{ flex:none; height:34%; }
    body.playing.vidfocus #videowrap{ flex:1; width:100%; height:auto; }   /* video-focus: video fills the column, roll -> PiP */
    body.playing.vidfocus #videowrap video{ width:100%; height:100%; }
    body.playing .bar{ margin:6px 0 0; flex:none; } body.playing .times{ flex:none; }
    body.playing #keyswrap{ flex:none; }
    /* portrait: full-width aligned rows, a calm block instead of a ragged wrap. play + speed on row 1,
       audio + note length on row 2 (audio natural width, the bare slider fills the rest). */
    body.playing .transport{ flex-direction:column; flex-wrap:nowrap; align-items:stretch; gap:9px; flex:none; }
    body.playing .transport .trow{ display:flex; gap:9px; align-items:stretch; }
    body.playing .transport .grp{ gap:0; }
    body.playing .grpspeed{ flex:1; }
    body.playing .grpspeed .seg{ display:flex; } body.playing .grpspeed .seg button{ flex:1; text-align:center; }
    body.playing .pp{ width:40px; height:40px; }
    body.playing .hint{ display:none; }
  }
  /* any short viewport (landscape phone, or a short-and-wide resized window): full-height flex — video+roll fill
     the space left, everything else pins, so it always fits. this owns ALL heights <=500px (the portrait
     split-keyboard stack needs more vertical room than that), so a narrow-short window gets the space-efficient
     side-by-side layout instead of a collapsed-video portrait. the portrait rule above now carries a min-height
     guard so the two never collide. transport wraps so it survives narrow widths too. */
  @media (max-height:500px){
    html,body{ height:100%; }
    #draghint { font-size:11.5px; padding:.5em .95em; }
    .wrap{ height:100dvh; gap:6px;
      padding:max(6px,env(safe-area-inset-top)) max(14px,env(safe-area-inset-right)) max(8px,env(safe-area-inset-bottom)) max(14px,env(safe-area-inset-left)); }
    header{ display:none; }   /* reclaim the row: in landscape you're on the video, not the app name — the clip name rides the Library row instead */
    .navclip{ display:block; min-width:0; font:italic 11px var(--serif); color:var(--ink2); white-space:nowrap; overflow:hidden; text-overflow:ellipsis; }
    #stage{ flex:1; min-height:0; gap:4px; }
    .back{ margin:0; padding:0; font-size:11px; flex:none; }
    .topgrid{ flex:1; min-height:0; }
    #videowrap,#rollwrap{ height:100%; }
    video{ max-height:none; height:100%; object-fit:contain; }
    #keyswrap{ height:54px; flex:none; }
    .bar{ margin:2px 0; flex:none; } .times{ font-size:10px; flex:none; }
    .transport{ gap:6px 8px; flex-wrap:wrap; flex:none; } .grp{ gap:2px; } .grp .cap{ font-size:8px; }
    .seg button,.loop button{ padding:5px 8px; } .seg button,.loop button,#tval{ font-size:11px; } .pp{ width:36px; height:36px; }
    .hint{ display:none; }
  }
  /* any wide screen while playing (>640px, taller than phone-landscape): fit one screen with full-height
     flex so the transport never clips. the default flow layout caps the video at 52vh, which overflows for
     a PORTRAIT clip (openstudio) once you add ~440px of chrome — until the window is ~917px+ tall. flex +
     object-fit:contain makes the video shrink to fit regardless of aspect, so it can't overflow at any
     height. keeps the header + full keyboard (gentler than phone landscape). min-height guard so it doesn't
     fight the phone-landscape rules above. */
  @media (min-height:501px) and (min-width:641px){
    html,body{ height:100%; }
    body.playing{ overflow:hidden; }
    body.playing .wrap, html.deeplink body:not(.playing) .wrap{ height:100dvh; }   /* deep-link load uses the SAME full-height layout so the panes fill the same space (no resize when the clip arrives) */
    body.playing #stage, html.deeplink body:not(.playing) #stage{ flex:1; min-height:0; }
    body.playing .topgrid, html.deeplink body:not(.playing) .topgrid{ flex:1; min-height:0; }
    body.playing #videowrap, body.playing #rollwrap,
    html.deeplink body:not(.playing) #videowrap, html.deeplink body:not(.playing) #rollwrap{ height:100%; }
    body.playing video{ max-height:none; height:100%; object-fit:contain; }
    body.playing #keyswrap{ flex:none; }
    body.playing .transport{ flex:none; }
    body.playing .hint{ display:none; }
  }
  /* desktop edit/review: widen the app so the ROLL (the editing surface) gets real room, but keep the
     keyboard + progress bar + transport controls at a readable width — a full-width 88-key strip and
     stretched-out controls are hard to read. So the roll expands; everything below it stays centered at 940. */
  @media (min-width:941px){
    body.playing.editing .wrap { max-width:min(1680px, 97vw); }
    body.playing.editing #videowrap { flex:none; width:clamp(200px, 20%, 320px); }
    body.playing.editing #keyswrap,
    body.playing.editing .barrow,
    body.playing.editing .transport { align-self:center; width:100%; max-width:940px; }
  }
  /* video fit/fill toggle — placed after the layouts so it wins over their object-fit:contain */
  body.crop video { object-fit:cover; }

  /* note-length: a single "shorter <-> longer" slider group in the transport (injected by player.js) */
  .lenwrap { display:flex; flex-direction:column; gap:3px; position:relative; }
  /* detent at legato (slider value 75): a small tick just BELOW the track, centred on the thumb's legato
     position (width-independent). Sits in the gap between the track and the shorter/longer labels. */
  .lennotch { position:absolute; top:16px; left:calc(7.5px + 0.75 * (100% - 15px)); transform:translateX(-50%);
    width:2px; height:4px; border-radius:1px; background:var(--ink3); pointer-events:none; }
  .lennotch.orig { left:calc(7.5px + 0.5 * (100% - 15px)); background:var(--acc); }   /* "original" (as-recorded) detent, centered on value 50 */
  .lenscale span:nth-child(2){ position:absolute; left:50%; transform:translateX(-50%); }   /* center the "original" label under its detent */
  .lenrng { -webkit-appearance:none; appearance:none; width:148px; height:4px; border-radius:999px; background:var(--line); outline:none; cursor:pointer; }
  .lenrng::-webkit-slider-thumb { -webkit-appearance:none; appearance:none; width:15px; height:15px; border-radius:50%; background:var(--paper); border:1px solid var(--line2); box-shadow:0 1px 3px rgba(60,50,30,.3); cursor:pointer; }
  .lenscale { position:relative; display:flex; justify-content:space-between; font:600 8.5px var(--sans); text-transform:uppercase; letter-spacing:.05em; color:var(--ink3); }
  @media (max-height:500px){ .lenrng { width:104px; } .lenscale { display:none; } }   /* compact in the tight landscape transport; at 104px the three caption words overlap into soup, and the detent notches already mark original/legato */

  /* ---- MIDI edit mode chrome (mode toggle + the floating toolbar over the roll) ---- */
  .modes { display:inline-flex; border:1px solid var(--line2); border-radius:9px; overflow:hidden; background:var(--panel); }
  .modes button { border:0; background:none; color:var(--ink2); font:600 12.5px var(--sans); padding:8px 15px; cursor:pointer; border-right:1px solid var(--line2); }
  .modes button:last-child { border-right:0; }
  .modes button.on { background:var(--card); color:var(--acc-d); }
  /* edit toolbar FLOATS over the roll's bottom-left (edit mode only) so entering edit never reflows the
     page. Translucent + blur so the notes underneath stay readable. */
  #etbar { position:absolute; left:10px; bottom:10px; z-index:5; gap:6px; flex-wrap:wrap;
    max-width:calc(100% - 20px); padding:6px; border-radius:11px;
    background:rgba(255,252,240,.86); border:1px solid var(--line2);
    box-shadow:0 6px 18px -8px rgba(16,15,15,.5); backdrop-filter:blur(5px); -webkit-backdrop-filter:blur(5px);
    visibility:hidden; }
  #etbar.on { visibility:visible; }
  /* review (Aria candidate labeler) floating bar — same treatment as the edit card */
  #revbar { position:absolute; left:10px; right:10px; top:10px; z-index:5; gap:7px; flex-wrap:wrap;
    padding:8px 10px; border-radius:11px; background:rgba(255,252,240,.94); border:1px solid var(--line2);
    box-shadow:0 6px 18px -8px rgba(16,15,15,.5); backdrop-filter:blur(5px); -webkit-backdrop-filter:blur(5px);
    visibility:hidden; }   /* review bar at the TOP; edit toolbar stays at the bottom -> no collision */
  #revbar.on { visibility:visible; }
  #revbar .revinfo { font:600 14px ui-monospace,monospace; color:var(--ink); }
  #revbar .revprog { font:600 12px var(--sans); color:var(--ink3); margin-right:auto; }
  #revbar .tool, #revbar .save { padding:8px 12px; font-size:13px; }
  #revbar #revNo { color:#AF3029; border-color:#e0b3ad; }
  body.reviewing .rollzoom, body.reviewing #dlmidi { display:none; }   /* the top-right roll controls would sit under the review bar */
  /* off-screen-note count chevrons, centered at the edges of the note window (edit mode) */
  .rollchev { position:absolute; left:50%; transform:translateX(-50%); z-index:6; display:none;
    align-items:center; gap:5px; height:22px; padding:0 11px; border-radius:999px;
    background:rgba(255,252,240,.94); border:1px solid var(--line2); color:var(--ink2);
    font:600 11px var(--sans); cursor:pointer; box-shadow:0 2px 9px -4px rgba(16,15,15,.4); }
  .rollchev:hover { border-color:var(--ink3); color:var(--ink); }
  .rollchev.up { top:8px; } .rollchev.dn { bottom:64px; }   /* dn sits just above the reserved toolbar strip / floating card */
  #etbar .tool { padding:6px 10px; font-size:12px; background:var(--paper); }
  #etbar .tool.ic { padding:6px 8px; }   /* icon-only undo/redo: compact square so the floating card fits one row in the 440px roll pane */
  #etbar .etstatus { font:600 11px var(--sans); color:var(--acc-d); align-self:center; padding:0 4px; min-width:52px; }   /* auto-save status ("Saving…"/"Saved"); no button, edits persist on their own */
  #etbar .etcheck { display:inline-flex; align-items:center; gap:5px; font:600 12px var(--sans); color:var(--ink2); cursor:pointer; padding:0 4px; user-select:none; }
  #etbar .etcheck input { accent-color:var(--acc); width:14px; height:14px; cursor:pointer; }
  #etbar .save { padding:7px 12px; font-size:12px; }
  .tbar { display:flex; align-items:center; gap:8px; }
  .tbar .tool { display:inline-flex; align-items:center; gap:7px; font:600 13px var(--sans); color:var(--ink2); background:var(--card); border:1px solid var(--line2); border-radius:8px; padding:8px 12px; cursor:pointer; }
  .tbar .tool:hover { border-color:var(--ink3); color:var(--ink); }
  .tbar .tool.on { background:rgba(102,128,11,.13); border-color:var(--acc); color:var(--acc-d); }
  .tbar .tool svg { width:16px; height:16px; display:block; }
  .tbar .etsp { flex:1; }
  .tbar .save { display:inline-flex; align-items:center; gap:7px; font:600 13px var(--sans); color:var(--acc-ink); background:var(--acc); border:1px solid var(--acc-d); border-radius:8px; padding:9px 15px; cursor:pointer; }
  .tbar .save:hover { background:var(--acc-d); }

  /* A/B verify bar (edit mode) — hear this chunk real, then as notes, then fix */
  .abbar { display:flex; align-items:center; gap:12px; flex-wrap:wrap; margin:12px 0 2px;
    padding:10px 12px; background:var(--paper); border:1px solid var(--line2); border-radius:12px; }
  .abbar .abgo { font:600 15px var(--sans); color:var(--acc-ink); background:var(--acc); border:none;
    border-radius:10px; padding:11px 20px; cursor:pointer; min-width:186px; text-align:center; transition:background .12s; }
  .abbar .abgo:hover { background:var(--acc-d); }
  .abbar .abgo.playing { background:var(--b); }
  .abbar .abstep { font:600 13px var(--sans); color:var(--ink2); background:transparent; border:1px solid var(--line2);
    border-radius:9px; padding:9px 13px; cursor:pointer; }
  .abbar .abstep:hover { border-color:var(--ink3); color:var(--ink); }
  .abbar .grp { display:flex; align-items:center; gap:8px; }
  .abbar .abwhich { font:italic 13px var(--serif); color:var(--ink2); }
  .abbar .abcount { margin-left:auto; font:italic 13px var(--serif); color:var(--ink2); }
  @media (max-width:640px){ .abbar .abwhich { display:none; } }
  /* ---- mini piano-roll thumbnail (renderMiniRoll): DOM bars, no <canvas> — geometry from _mockup-plans-c.html ---- */
  .mr { position:relative; background:var(--roll); overflow:hidden; flex:none; border-radius:6px; }
  .mr .lane { position:absolute; left:0; right:0; height:1px; background:rgba(16,15,15,.05); }
  .mr .loopt { position:absolute; top:0; bottom:0; background:rgba(102,128,11,.10); }
  .mr .n { position:absolute; height:4px; border-radius:1px; background:var(--ink); opacity:.82; }

  /* ---- Save-spot: corner star on the roll pane + overlay label capture (no prompt(), so it's headless-
     testable; the overlay is absolutely positioned inside #rollwrap so opening/saving NEVER reflows the page) ---- */
  /* Own styles (not .dlmidi) so the waterfall's `body.waterfall .dlmidi { display:none }` doesn't take
     capture with it — the star exists in BOTH roll views; only its shape changes (see the waterfall block). */
  .savespot { position:absolute; right:88px; top:8px; width:34px; height:34px; padding:0; border-radius:8px; z-index:4; background:rgba(255,252,240,.82); border:1px solid var(--line2); color:var(--ink2); place-items:center; cursor:pointer; transition:color .15s, border-color .15s, background .15s; }   /* sits left of dlmidi(48); both left of the zoom(8). display is JS-managed (grid when a workspace clip is open) */
  .savespot:hover { color:var(--acc-d); border-color:var(--acc); background:rgba(102,128,11,.1); }
  .savespot svg { width:16px; height:16px; display:block; }
  .savespot .slbl { display:none; }   /* classic corner button is icon-only; the falling view shows the label */
  .saveinline { display:flex; align-items:center; gap:8px; }
  .saveinline input { flex:1; min-width:0; font:600 13px var(--sans); padding:9px 11px; border-radius:8px;
    border:1px solid var(--acc); background:var(--card); color:var(--ink); }
  .saveinline input:focus { outline:none; border-color:var(--acc-d); }
  .saveinline button.ok { flex:none; background:var(--acc); border:1px solid var(--acc-d); color:var(--acc-ink); font:700 13px var(--sans); padding:8px 13px; border-radius:8px; cursor:pointer; }
  .saveinline button.ghost { flex:none; background:none; border:1px solid var(--line2); color:var(--ink2); font:600 13px var(--sans); padding:8px 11px; border-radius:8px; cursor:pointer; }
  .saveoverlay { position:absolute; top:50px; right:8px; z-index:5; width:min(320px, calc(100% - 16px));
    background:var(--paper); border:1px solid var(--line2); border-radius:10px; padding:8px;
    box-shadow:0 6px 18px rgba(16,15,15,.12); }

  /* ---- home Spots row: mini clip-cards — the frame at the saved moment, the time as the grid's own
     corner badge, and (for range spots with notes) a slim piano-roll strip on the poster's bottom edge ---- */
  .homespots { margin:2px 0 6px; }
  .homespots .lbl { font:700 10px var(--sans); letter-spacing:.12em; text-transform:uppercase; color:var(--ink3); margin-bottom:9px; }
  .spotmini { width:118px; background:var(--card); border:1px solid var(--line); border-radius:12px; overflow:hidden;
    cursor:pointer; box-shadow:0 1px 2px rgba(60,50,30,.05); transition:border-color .12s; }
  .spotmini:hover { border-color:var(--acc); }
  .spotmini .ph { position:relative; aspect-ratio:3/4; background:var(--roll); overflow:hidden; }
  .spotmini .ph video { position:absolute; inset:0; width:100%; height:100%; object-fit:cover; display:block; }
  .spotmini .ph .dur { position:absolute; right:6px; bottom:6px; font:600 10.5px var(--sans); color:#fff;
    background:rgba(16,15,15,.6); padding:2px 6px; border-radius:5px; backdrop-filter:blur(3px); z-index:2; }
  .spotmini .ph .mrstrip { position:absolute; left:0; right:0; bottom:0; height:20px; z-index:1; }
  .spotmini .ph .mrstrip + .dur { bottom:26px; }   /* the badge rides above the note strip when one exists */
  .spotmini .ph .mrstrip.mr { background:rgba(242,240,229,.92); border-radius:0; }
  .spotmini .ph .mrstrip .n { height:2px; border-radius:0.5px; }   /* teeny — separation beats bulk at this size; the contour is the signature */
  .spotmini .body { padding:7px 9px 8px; }
  .spotmini .t1 { font-family:var(--serif); font-size:13px; line-height:1.25; display:-webkit-box; -webkit-line-clamp:2; -webkit-box-orient:vertical; overflow:hidden; }
  .spotmini .t3 { font:italic 11px var(--serif); color:var(--ink2); margin-top:2px; white-space:nowrap; overflow:hidden; text-overflow:ellipsis; }
  .filmrow { display:flex; gap:11px; flex-wrap:wrap; }
  .filmchip { width:150px; border:1px solid var(--line2); border-radius:9px; overflow:hidden; cursor:pointer; background:var(--card); transition:border-color .12s; }
  .filmchip .mr { width:100%; height:56px; }
  .filmchip .fm { padding:7px 10px; }
  .filmchip .fl { font-weight:600; font-size:12.5px; white-space:nowrap; overflow:hidden; text-overflow:ellipsis; }
  .filmchip .fr { font:11px ui-monospace,Menlo,monospace; color:var(--ink3); margin-top:1px; }
  .filmchip:hover { border-color:var(--acc); }

  /* ---- Plans list: each plan previews as tiny mini-rolls of its first couple of spots ---- */
  .plist { display:flex; flex-direction:column; gap:11px; }
  .prow { display:flex; align-items:center; gap:16px; background:var(--card); border:1px solid var(--line2); border-radius:12px; padding:12px 16px; cursor:pointer; transition:border-color .12s; }
  .prow:hover { border-color:var(--acc); }
  .prow .pv { display:flex; gap:4px; flex:none; }
  .prow .pv .mr { width:38px; height:26px; border-radius:4px; }
  .prow .info { flex:1; min-width:0; }
  .prow .pn { font-family:var(--serif); font-weight:600; font-size:16px; white-space:nowrap; overflow:hidden; text-overflow:ellipsis; }
  .prow .pc { font:11px ui-monospace,Menlo,monospace; color:var(--ink3); margin-top:2px; }
  .newp { border:1px dashed var(--line2); border-radius:12px; padding:15px; text-align:center; color:var(--acc-d); font-weight:600; cursor:pointer; }
  .newp:hover { border-color:var(--acc); background:rgba(102,128,11,.06); }

  /* ---- Plan doc: editable title + ordered text/spot blocks (composable, whole-doc save) ---- */
  .ttl { font-family:var(--serif); font-size:25px; font-weight:600; letter-spacing:-.01em; outline:none; border-radius:6px; padding:2px 4px; margin:-2px -4px 18px; }
  .ttl:focus { background:var(--panel); }
  .doc p { font-family:var(--serif); font-size:16px; line-height:1.62; color:var(--ink); margin:16px 2px; outline:none; border-radius:6px; padding:2px 4px; }
  .doc p:focus { background:var(--panel); }
  .doc p:empty:before { content:attr(data-ph); color:var(--ink3); }
  .blk { position:relative; }
  .blk .rm { position:absolute; top:4px; right:-2px; width:22px; height:22px; border-radius:50%; border:1px solid var(--line2);
    background:var(--card); color:var(--ink3); font-size:14px; line-height:1; cursor:pointer; display:grid; place-items:center;
    opacity:0; transition:opacity .12s, border-color .12s, color .12s; }
  .blk:hover .rm { opacity:1; }
  .blk .rm:hover { border-color:var(--acc); color:var(--acc-d); }
  .band { display:flex; gap:0; margin:12px 0; border-radius:11px; overflow:hidden; border:1px solid var(--line2); cursor:pointer;
    background:var(--card); transition:border-color .12s, box-shadow .12s; }
  .band .mr { width:152px; height:80px; }
  .band .meta { padding:0 18px; display:flex; flex-direction:column; justify-content:center; gap:4px; flex:1; min-width:0; }
  .band .lab { font-weight:600; font-size:15.5px; }
  .band .rng { font:12px ui-monospace,Menlo,monospace; color:var(--ink2); display:flex; gap:9px; align-items:center; }
  .band .loop { font:600 9px var(--sans); letter-spacing:.07em; color:var(--acc-d); background:rgba(102,128,11,.14); border-radius:4px; padding:2px 6px; text-transform:uppercase; }
  .band .clip { color:var(--ink3); font:italic 12px var(--serif); }
  .band .drag { align-self:center; color:var(--ink3); padding:0 14px 0 4px; font-size:15px; letter-spacing:-2px; cursor:grab; }
  .band .drag:active { cursor:grabbing; }
  .band:hover { border-color:var(--acc); box-shadow:0 3px 12px -6px rgba(102,128,11,.5); }
  .band.tomb { cursor:default; background:var(--panel); }
  .band.tomb .lab { color:var(--ink2); }
  .band.tomb .rng { font-family:var(--sans); font-style:italic; }
  .band.tomb:hover { border-color:var(--line2); box-shadow:none; }
  .add { display:flex; gap:12px; margin-top:18px; }
  .add span { font:600 12px var(--sans); color:var(--ink3); border:1px dashed var(--line2); border-radius:8px; padding:8px 13px; cursor:pointer; }
  .add span:hover { color:var(--acc-d); border-color:var(--acc); }

  /* ---- Insert-a-spot picker: full-screen overlay listing every saved spot as a mini-roll chip ---- */
  .picker { display:none; position:fixed; inset:0; background:rgba(16,15,15,.45); z-index:70; align-items:flex-start; justify-content:center; padding:60px 16px; overflow-y:auto; }
  .picker.on { display:flex; }
  .pickersheet { width:100%; max-width:640px; background:var(--paper); border:1px solid var(--line2); border-radius:14px; padding:22px; }
  .pickerhead { display:flex; align-items:center; justify-content:space-between; font:600 15px var(--sans); margin-bottom:16px; }
  .pickerhead button { font:600 12px var(--sans); padding:7px 12px; border-radius:8px; border:1px solid var(--line2); background:none; color:var(--ink2); cursor:pointer; }
  .pickerhead button:hover { border-color:var(--acc); color:var(--acc-d); }
  .pickerempty { padding:12px 2px; }

  /* Waterfall view (single-row-keyboard layouts: landscape phone + desktop): the roll fills the top area and notes fall onto the keyboard;
     the video shrinks to a top-right PiP. Pane-zoom modes don't compose (applyWaterfallUI clears them).
     Toggle sits top-LEFT of the roll so the PiP owns the right corner.
     This block lives at the END of the file: the desktop/landscape layout media queries use selectors of
     equal specificity (body.playing #videowrap), so the waterfall rules must come later to win. */
  .wfview { position:absolute; left:8px; top:8px; display:flex; border:1px solid var(--line2); border-radius:8px;
    overflow:hidden; background:rgba(255,252,240,.92); z-index:4; }
  .wfview button { font:600 10.5px var(--sans); letter-spacing:.02em; color:var(--ink2); background:none; border:0;
    padding:5px 9px; cursor:pointer; display:flex; gap:5px; align-items:center; transition:.15s; }
  .wfview button:hover { color:var(--ink); }
  .wfview button.on { background:rgba(102,128,11,.14); color:var(--acc-d); }
  .wfview svg { width:11px; height:11px; display:block; }
  body.waterfall .topgrid { position:relative; }
  /* On the PLAYER in any landscape window, the chrome yields — header gone, slim nav, full-height
     stage — in BOTH views (classic + waterfall): the clip is the point, the app shell is not.
     (Phone-landscape <=500px already had this via its media block; this extends it to every
     wider-than-tall window while a clip is open. body.onstage = the stage is showing.) */
  @media (orientation: landscape){
    body.onstage { height:100dvh; overflow:hidden; }
    body.onstage header { display:none; }
    body.onstage .wrap { height:100dvh; gap:6px;
      padding:max(6px,env(safe-area-inset-top)) max(14px,env(safe-area-inset-right)) max(8px,env(safe-area-inset-bottom)) max(14px,env(safe-area-inset-left)); }
    body.onstage #stage { flex:1; min-height:0; gap:4px; }
    body.onstage .navrow { margin:0; }
    body.onstage .back { margin:0; padding:0; font-size:11px; flex:none; }
    body.onstage .navclip { display:block; min-width:0; font:italic 11px var(--serif); color:var(--ink2); white-space:nowrap; overflow:hidden; text-overflow:ellipsis; }
    body.onstage .topgrid { flex:1; min-height:0; }
    body.onstage #videowrap, body.onstage #rollwrap { height:100%; }
    body.onstage video { max-height:none; height:100%; object-fit:contain; }
    body.onstage #keyswrap { flex:none; }
    body.onstage .bar { margin:2px 0; flex:none; } body.onstage .times { flex:none; }
    body.onstage .transport { flex:none; }
    body.onstage .hint { display:none; }
  }
  /* waterfall keeps the tighter phone-style compaction at every size (it is the most focused view) */
  body.waterfall { height:100dvh; overflow:hidden; }
  body.waterfall header { display:none; }
  body.waterfall .wrap { height:100dvh; gap:6px;
    padding:max(6px,env(safe-area-inset-top)) max(14px,env(safe-area-inset-right)) max(8px,env(safe-area-inset-bottom)) max(14px,env(safe-area-inset-left)); }
  body.waterfall #stage { flex:1; min-height:0; gap:4px; }
  body.waterfall .navrow { margin:0; }
  body.waterfall .back { margin:0; padding:0; font-size:11px; flex:none; }
  body.waterfall .navclip { display:block; min-width:0; font:italic 11px var(--serif); color:var(--ink2); white-space:nowrap; overflow:hidden; text-overflow:ellipsis; }
  body.waterfall .topgrid { flex:1; min-height:0; }
  body.waterfall #rollwrap { height:100%; }
  body.waterfall #keyswrap { height:54px; flex:none; }
  body.waterfall .bar { margin:2px 0; flex:none; } body.waterfall .times { font-size:10px; flex:none; }
  body.waterfall .transport { gap:6px 8px; flex-wrap:wrap; flex:none; }
  body.waterfall .grp { gap:2px; } body.waterfall .grp .cap { font-size:8px; }
  body.waterfall .seg button, body.waterfall .loop button { padding:5px 8px; }
  body.waterfall .seg button, body.waterfall .loop button, body.waterfall #tval { font-size:11px; }
  body.waterfall .pp { width:36px; height:36px; }
  body.waterfall .lenrng { width:104px; }
  body.waterfall .hint { display:none; }
  /* the creator's video is the point: a big FREE-floating panel. It may overlap anything (roll, keys,
     transport) because it's movable anywhere — position:fixed, left/top driven by JS (placePip),
     position remembered as window fractions. */
  body.waterfall #videowrap { position:fixed; z-index:20; right:8px; --piph:calc((100dvh - 20px) * var(--pipscale, 0.7));
    top:calc(8px + (100dvh - 16px - var(--piph)) * var(--pipfy, 0.1)); height:var(--piph); width:auto; aspect-ratio:9/16; flex:none;
    border-radius:10px; overflow:hidden; box-shadow:0 8px 26px -6px rgba(16,15,15,.6); cursor:grab; touch-action:none; }
  /* portrait-aspect windows: a full-height 9:16 panel would be wider than half the window and drown it —
     tie the panel to the width instead (40vw wide via the aspect ratio) */
  @media (orientation: portrait){
    body.waterfall #videowrap { --piph:calc(min(calc(100dvh - 20px), calc(48vw * 16 / 9)) * var(--pipscale, 0.7)); }
  }
  /* Whenever the window can fit BOTH, it absolutely should: reserve a right gutter the width of the
     (full-height) panel so the app column and the creator's video sit side by side with no overlap.
     JS toggles .wfdock when app width + panel width fits (placePip); narrower windows float/overlap
     as usual, with the ghost notes covering the overlap. The panel stays draggable either way. */
  body.waterfall.wfdock .wrap { margin-right:calc((100dvh - 175px) * 9 / 16 + 24px); }   /* pre-JS approximation; placePip sets the exact margin for the clip's real aspect */
  /* The focused chrome only needs PHONE-tight metrics on short screens (mobile landscape, where it was
     designed); tall (desktop) windows keep the focused layout at comfortable scale — bigger nav, real
     breathing room under the nav/modes row, full-size transport. */
  @media (min-height:501px){
    body.onstage .navrow, body.waterfall .navrow { margin:2px 0 6px; }
    body.onstage .back, body.waterfall .back { font-size:13px; }
    body.onstage .navclip, body.waterfall .navclip { font:italic 13px var(--serif); }
    body.onstage #stage, body.waterfall #stage { gap:12px; }
    body.onstage .wrap, body.waterfall .wrap { gap:12px; padding-top:max(14px,env(safe-area-inset-top)); }
    body.waterfall .transport { gap:10px 14px; }
    body.waterfall .grp .cap { font-size:9.5px; }
    body.waterfall .seg button, body.waterfall .loop button, body.waterfall #tval { font-size:12px; }
    body.waterfall .seg button, body.waterfall .loop button { padding:6px 10px; }
    body.waterfall .pp { width:42px; height:42px; }
    body.waterfall .lenrng { width:148px; }   /* full-size slider fits the roomy desktop transport, so the caption words fit too */
    body.waterfall .bar { margin:4px 0; } body.waterfall .times { font-size:11px; }
  }
  /* deep-link shimmer sizes panes with body:not(.playing) rules; the waterfall panel must keep its own
     height from first paint (no 820->800 pop when playing starts) */
  html.deeplink body.waterfall:not(.playing) #videowrap { height:var(--piph); }
  #vnotes { display:none; }   /* the see-through notes layer exists only over the waterfall PiP */
  body.waterfall #vnotes { display:block; position:absolute; inset:0; width:100%; height:100%; pointer-events:none; z-index:2; }
  body.waterfall #videowrap video { height:100%; width:100%; max-height:none; object-fit:contain; }   /* panel matches the clip's aspect, so no letterbox in practice; contain = never crop captions during the metadata settle */
  body.waterfall .vcrop, body.waterfall .vidzoom, body.waterfall .rollzoom { display:none; }
  .vsize { display:none; }   /* the size control exists only on the waterfall PiP */
  body.waterfall #videowrap .vsize { display:grid; place-items:center; position:absolute; left:10px; top:10px; width:34px; height:34px; z-index:4; padding:0;
    border-radius:8px; background:rgba(16,15,15,.45); border:none; color:#fff; cursor:pointer; transition:background .15s; }
  body.waterfall #videowrap .vsize:hover { background:rgba(16,15,15,.65); }
  body.waterfall #videowrap .vsize svg { width:16px; height:16px; display:block; }
  body.waterfall .dlmidi { display:none !important; }   /* the PiP owns that corner (JS manages its inline display) */
  /* capture parity in the falling view: the corner icons are hidden here by design, so the star joins
     the top-left cluster as a labeled pill in the Falling|Scrolling toggle's style family, right of it
     (placeSaveStar computes the left offset). The label overlay opens just below the cluster. */
  body.waterfall .savespot { right:auto; top:8px; width:auto; height:auto; padding:5px 9px; grid-auto-flow:column; align-items:center; gap:5px;
    background:rgba(255,252,240,.92); font:600 10.5px var(--sans); letter-spacing:.02em; }
  body.waterfall .savespot svg { width:11px; height:11px; }
  body.waterfall .savespot .slbl { display:inline; }
  body.waterfall .savespot:hover { color:var(--ink); border-color:var(--line2); background:rgba(255,252,240,.92); }   /* the cluster family hovers to ink, not moss */
  body.waterfall .saveoverlay { top:44px; left:8px; right:auto; bottom:auto; }
  body.waterfall #draghint { display:none; }      /* the coach-mark's gesture (and arrows) are horizontal-roll only */
  body.waterfall #rollwrap { width:100%; }
  /* notes must land ON the keys: keyboard rides directly under the roll; scrubber + transport drop below it */
  body.waterfall #keyswrap { order:1; }
  body.waterfall .barrow { order:2; } body.waterfall .transport { order:3; }
