  :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 .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; }
  .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; top:10px; width:34px; height:34px; 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:var(--acc); border-color:var(--acc-d); }
  .vcrop svg { width:16px; height:16px; }
  .vplay { position:absolute; left:12px; bottom:12px; width:42px; height:42px; border-radius:50%; background:rgba(16,15,15,.5); border:none; display:grid; place-items:center; cursor:pointer; transition:background .15s; }
  .vplay:hover { background:var(--acc); }
  .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;} }
  #keyswrap { height:78px; }
  #keys { width:100%; height:100%; display:block; background:var(--line2); border:1px solid var(--line2); border-radius:8px; }
  #keyssplit { display:none; }

  .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:-6px; height:calc(100% + 12px); cursor:ew-resize; touch-action:none; z-index:3; display:flex; align-items:center; }
  .bar .marker::before, .bar .marker::after { content:''; display:block; }
  .bar .mA::before { width:3px; height:100%; border-radius:1px; background:var(--ink); }                          /* | at loop start */
  .bar .mA::after  { width:0; height:0; border:7px solid transparent; border-left:9px solid var(--ink); border-right:0; }  /* > pointing in */
  .bar .mB { transform:translateX(-100%); }                                                                       /* right edge sits on loopB */
  .bar .mB::before { width:0; height:0; border:7px solid transparent; border-right:9px solid var(--ink); border-left:0; } /* < pointing in */
  .bar .mB::after  { width:3px; height:100%; border-radius:1px; background:var(--ink); }                          /* | at loop end */
  .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; }
  .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 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:4px; height:15px; border:0; background:var(--acc-ink); box-shadow:7px 0 0 var(--acc-ink); margin-left:-3px; }
  .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; }
  .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); }
  /* 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; }
  }
  .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 */
  .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; }
  #drop .big { font-family:var(--serif); }
  #cards { display:flex; flex-direction:column; }
  .shelfhead { font:650 12px var(--sans); text-transform:uppercase; letter-spacing:.07em; color:var(--ink2); margin:14px 0 11px; display:flex; gap:7px; align-items:center; }
  .shelfhead .n { color:var(--ink3); font-weight:600; }
  .shelf { display:flex; gap:14px; overflow-x:auto; padding-bottom:10px; scrollbar-width:thin; }
  .shelf .card { flex:0 0 194px; }
  .shelf.grid { display:grid; grid-template-columns:repeat(auto-fill,minmax(178px,1fr)); overflow:visible; }
  .shelf.grid .card { flex:none; }
  .card { background:var(--card); border:1px solid var(--line2); border-radius:8px; padding:11px; cursor:pointer; transition:box-shadow .12s, transform .12s; }
  .card:hover { box-shadow:0 5px 14px rgba(60,50,30,.11); transform:translateY(-2px); }
  .card .frame { border-radius:5px; overflow:hidden; aspect-ratio:16/10; background:#000; border:1px solid var(--line2); }
  .card .frame video { width:100%; height:100%; object-fit:cover; display:block; }
  .card.processing { cursor:default; }
  .card.processing .frame { background:var(--roll); display:grid; place-items:center; }
  .card .tt { font-family:var(--serif); font-size:16px; margin:11px 3px 6px; letter-spacing:-.005em; line-height:1.15; }
  .card .tm { display:flex; align-items:center; gap:8px; margin:0 3px; font-size:12.5px; color:var(--ink2); }
  .card .tm .k { color:var(--acc); font-weight:650; }
  .card .tm .d { text-transform:capitalize; }
  .card .tm .rule { width:1px; height:11px; background:var(--line); }
  .card .tm .len { font-variant-numeric:tabular-nums; }
  .card .star { cursor:pointer; color:var(--ink3); font-size:15px; line-height:1; margin-left:auto; }
  .card .star.on { color:var(--acc); }
  .card .proclabel { color:var(--ink2); font-size:12px; margin-top:9px; text-align:center; }
  .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 */
  .card.jobwork .frame, .card.jobfail .frame { position:relative; display:grid; place-items:center; }
  .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 3px 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 .frame { background:var(--roll); }
  .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; }
  .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 #wscopy { 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); }
  #vspin { position:absolute; inset:0; display:none; place-items:center; z-index:6; }
  #vspin.on { display:grid; }
  #vspin .spin { width:28px; height:28px; border-width:3px; border-color:rgba(255,255,255,.28); border-top-color:#fff; }
  @media (max-width:640px) and (min-height:501px){
    .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:auto; }
    #keys{ display:none; }
    #keyssplit{ display:flex; flex-direction:column; gap:8px; }
    #keyssplit .krow{ display:flex; }
    #keyssplit .krow.l{ justify-content:flex-start; } #keyssplit .krow.r{ justify-content:flex-end; }
    #keyssplit canvas{ display:block; height:58px; background:var(--line2); border:1px solid var(--line2); border-radius:6px; }
    #keysT{ width:85%; } #keysB{ width:67.4%; }   /* 85% × 23/29 white keys → matching key width across rows */

    /* 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 .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 #trow2 #audiorow{ flex:none; } body.playing #trow2 #audiorow .seg{ display:flex; }
    body.playing #trow2 .grp:last-child{ flex:1; display:flex; align-items:center; padding-left:2px; }   /* note length fills the rest */
    body.playing #trow2 .lenwrap{ width:100%; } body.playing #trow2 .lenrng{ width:100%; }
    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%; }
    .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; 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{ height:100dvh; }
    body.playing #stage{ flex:1; min-height:0; }
    body.playing .topgrid{ flex:1; min-height:0; }
    body.playing #videowrap, body.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; }
  }
  /* 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; }
  .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 { 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; } }   /* compact in the tight landscape transport */
