/* Vibe Check — Listen Later studio (producer). Apple-grade dark: base #0C0C0E, surface cards
   #1C1C1E/#2C2C2E, one blue #3B74F0 for actions, red #FF453A for expiry/destructive only.
   Token-driven so the whole surface follows :root. */
:root{
  --page:#0C0C0E; --card:#1C1C1E; --card-2:#2C2C2E; --line:#38383A; --line-2:rgba(255,255,255,.08);
  --ink:#ffffff; --ink-2:rgba(255,255,255,.60); --ink-3:rgba(255,255,255,.42); --ink-4:rgba(255,255,255,.55);
  /* --blue is NOT redeclared here. It was, at the old #3B74F0, which silently shadowed tokens.css
     and is why the product-wide blue change never reached the studio. The derivatives below follow
     the shared value. */
  --blue-press:#225AE2; --blue-soft:rgba(47,107,250,.18); --blue-edge:rgba(47,107,250,.40);
  --coral:#FF453A; --coral-line:rgba(255,69,58,.38); --coral-soft:rgba(255,69,58,.16);
  --sh-card:0 1px 3px rgba(0,0,0,.45);
  --sh-pop:0 24px 60px rgba(0,0,0,.55);
  --sh-blue:0 8px 20px rgba(59,116,240,.40);
  --r-card:22px; --r-panel:14px; --r-ctl:13px; --r-pill:999px;
  --f:'Inter',system-ui,-apple-system,BlinkMacSystemFont,sans-serif;
  --mono:'Roboto Mono',ui-monospace,monospace;
}
*{ box-sizing:border-box; -webkit-tap-highlight-color:transparent; }
[hidden]{ display:none !important; }
/* lock the app shell so overscroll can't pull-to-refresh / reload — the tracks list is the only scroller */
html,body{ margin:0; height:100%; overflow:hidden; overscroll-behavior:none; }
/* full-bleed: the .sb-stage (from sendbox.css) places the box top-left over the wallpaper.
   No body centering/padding — that's what was pushing the card to the middle. */
body{ background:var(--page); color:var(--ink); font-family:var(--f); -webkit-font-smoothing:antialiased; text-rendering:optimizeLegibility; margin:0; }
.wrap{ width:100%; max-width:600px; display:flex; flex-direction:column; gap:16px; }

/* lockup — wordmark is tracked text (no STREAKY; maker credit lives in copy) */
.lockup{ text-align:center; margin-top:2px; }
.wordmark{ height:22px; width:auto; display:inline-block; }
.logo-img{ display:block; margin:0 auto; width:200px; max-width:56%; height:auto; }
.modewrap{ display:none; }  /* no LISTEN LATER pill — mode is carried by content */

/* sheet (card) */
.sheet{ background:var(--card); border:1px solid var(--line); border-radius:var(--r-card); box-shadow:var(--sh-card); padding:30px 30px 24px; }
.muted{ color:var(--ink-2); font-size:13.5px; line-height:1.55; }

/* signed-out */
.signedout{ text-align:center; }
.signedout h2{ margin:14px 0 8px; font-size:19px; font-weight:700; color:var(--ink); }
.signedout .muted{ margin:0 auto 20px; max-width:400px; }
.bigGlyph{ color:var(--blue); display:flex; justify-content:center; }
.bigGlyph svg{ width:38px; height:38px; }

/* header row: section label left · usage opposite (right) */
.lhead{ display:flex; align-items:flex-start; justify-content:space-between; gap:20px; margin-bottom:14px; }
.sect{ font-family:var(--mono); font-size:11px; font-weight:500; letter-spacing:1px; text-transform:uppercase; color:var(--ink-3); padding-top:3px; }
.sect2{ font-family:var(--mono); font-size:11px; font-weight:500; letter-spacing:1px; text-transform:uppercase; color:var(--ink-3); margin:26px 0 2px; }

/* (the old header usage block died with the studio restructure — the meter now lives at the card's bottom: .sv-usage) */
.pbar{ height:6px; border-radius:4px; background:rgba(255,255,255,.12); overflow:hidden; }
.pbar > i{ display:block; height:100%; width:0%; background:var(--blue); border-radius:4px; transition:width .3s ease; }
.pbar > i.warn{ background:var(--coral); }

/* drop zone */
.drop{ display:block; border:1.5px dashed rgba(255,255,255,.20); border-radius:var(--r-panel); background:rgba(255,255,255,.04);
  cursor:pointer; transition:border-color .15s, background .15s; }
.drop:hover, .drop.drag{ border-color:var(--blue); background:rgba(59,116,240,.12); }
.drop.has{ border-style:solid; border-color:var(--blue); background:rgba(59,116,240,.12); }
.dropInner{ padding:26px 20px; text-align:center; }
.dropGlyph{ width:38px; height:38px; margin:0 auto 10px; display:flex; align-items:center; justify-content:center; color:var(--blue); }
.dropGlyph svg{ width:30px; height:30px; }
.dropText{ font-size:15px; font-weight:700; color:var(--ink); }
.dropText b{ color:var(--ink); }
.dropHint{ margin-top:4px; font-size:12.5px; color:var(--ink-2); }

/* fields */
.fields{ display:flex; flex-direction:column; gap:14px; margin-top:16px; }
.fieldRow{ display:flex; gap:14px; }
.fieldRow .field{ flex:1 1 0; min-width:0; }
.field{ display:flex; flex-direction:column; gap:7px; }
.field span{ font-size:11px; font-weight:600; color:var(--ink-3); text-transform:uppercase; letter-spacing:.04em; }
.field .cnt{ float:right; text-transform:none; letter-spacing:0; color:var(--ink-3); font-weight:500; }
.field input, .field select, .field textarea{ padding:0 13px; border-radius:var(--r-ctl); background:var(--card-2);
  border:1px solid var(--line); color:var(--ink); font:500 14px/1 var(--f); outline:none; width:100%; }
.field input, .field select{ height:44px; }
.field textarea{ min-height:62px; padding:12px 13px; line-height:1.45; resize:vertical; font-weight:500; }
.field input::placeholder, .field textarea::placeholder{ color:rgba(255,255,255,.30); }
.field select{ appearance:none; -webkit-appearance:none; cursor:pointer;
  background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='8' viewBox='0 0 12 8'%3E%3Cpath d='M1 1l5 5 5-5' stroke='%23ffffff' stroke-opacity='.5' stroke-width='1.6' fill='none' stroke-linecap='round'/%3E%3C/svg%3E");
  background-repeat:no-repeat; background-position:right 13px center; padding-right:34px; }
.field input:focus, .field select:focus, .field textarea:focus{ border-color:var(--blue); box-shadow:0 0 0 3px var(--blue-soft); }

/* a collapsed "Add a message" affordance that expands to the textarea */
.addmsg{ display:inline-flex; align-items:center; gap:7px; background:none; border:none; cursor:pointer; padding:2px 0;
  color:var(--blue); font:600 13px var(--f); }
.addmsg svg{ width:15px; height:15px; }

/* buttons */
.btn{ height:48px; display:inline-flex; align-items:center; justify-content:center; gap:9px; padding:0 16px;
  border-radius:14px; font:700 15px/1 var(--f); cursor:pointer; border:1px solid transparent; text-decoration:none;
  transition:background .18s ease, border-color .18s ease, color .18s ease, box-shadow .18s ease; white-space:nowrap; }
.btn svg{ width:17px; height:17px; flex:0 0 auto; }
.btn--primary{ background:var(--blue); color:#fff; box-shadow:var(--sh-blue); }
.btn--primary:hover{ background:var(--blue-press); }
.btn--primary:disabled{ background:rgba(255,255,255,.16); color:rgba(255,255,255,.45); box-shadow:none; cursor:default; }
.btn--secondary{ background:var(--card-2); color:var(--ink); border-color:var(--line); }
.btn--secondary:hover{ background:#3a3a3c; border-color:rgba(255,255,255,.22); }
.btn--secondary svg{ color:var(--ink-2); }
.btn--block{ width:100%; margin-top:18px; }
.btn.danger{ color:var(--coral); border-color:var(--coral-line); }
.btn.danger:hover{ background:var(--coral-soft); }

/* progress + messages */
.prog{ margin-top:14px; height:30px; border-radius:9px; background:rgba(255,255,255,.10); position:relative; overflow:hidden; }
.prog i{ display:block; height:100%; width:0%; background:var(--blue); transition:width .15s ease; }
.prog span{ position:absolute; inset:0; display:flex; align-items:center; justify-content:center; font-size:12px; font-weight:600; color:#fff; }
.formMsg{ margin-top:14px; font-size:13px; padding:10px 12px; border-radius:10px; }
.formMsg.err{ background:var(--coral-soft); color:#ff8a80; border:1px solid var(--coral-line); }
.formMsg.info{ background:var(--blue-soft); color:#9bb8f6; border:1px solid rgba(59,116,240,.30); }

/* link list */
.links{ margin-top:8px; }
.lrow{ display:flex; align-items:center; gap:12px; padding:14px 0; border-top:1px solid var(--line); }
.lrow .info{ flex:1; min-width:0; }
.lrow .lt{ font-size:16px; font-weight:700; color:var(--ink); white-space:nowrap; overflow:hidden; text-overflow:ellipsis; }
.lrow .lm{ font-size:13px; color:var(--ink-3); margin-top:2px; }
.lrow .lm .dot{ opacity:.55; margin:0 6px; width:auto; height:auto; border-radius:0; background:none; display:inline; }
.lrow .lm .exp{ color:var(--coral); font-weight:600; }
.lrow .lm .pin{ color:var(--ink-2); font-weight:600; }
.iconbtn{ width:30px; height:30px; flex:0 0 auto; border:1px solid var(--line); border-radius:8px; background:var(--card-2);
  display:flex; align-items:center; justify-content:center; color:var(--ink-3); cursor:pointer; text-decoration:none; transition:.15s; }
.iconbtn:hover{ background:#3a3a3c; color:var(--ink-2); }
.iconbtn.copied{ color:var(--blue); border-color:var(--blue-edge); }
.iconbtn svg{ width:14px; height:14px; }

/* Play button = circle with a blue ▶; tapping it (or the file name) opens the full player. */
.lrow .lt{ cursor:pointer; }
.lrow .playbtn{ width:40px; height:40px; border-radius:50%; border:1px solid var(--blue-edge); background:var(--card-2);
  box-shadow:0 4px 11px rgba(59,116,240,.18); color:var(--blue); }
.lrow .playbtn svg{ width:15px; height:15px; }
.lrow .playbtn .i-pause{ display:none; }

/* transient undo bar */
.undobar{ display:flex; align-items:center; justify-content:space-between; background:var(--card-2); border:1px solid var(--line);
  border-radius:10px; padding:12px 15px; margin-top:12px; font-size:13px; color:var(--ink-2); }
.undobar button{ background:none; border:none; color:var(--blue); font:600 13px var(--f); cursor:pointer; }
.empty{ color:var(--ink-3); font-size:13px; text-align:center; padding:18px; }

/* footer */
.foot{ text-align:center; color:var(--ink-3); font-size:12px; margin-top:2px; }
.foot a{ color:var(--blue); text-decoration:none; font-weight:600; }

/* ---------- Modal (link ready / edit) ---------- */
.overlay{ position:fixed; inset:0; background:rgba(0,0,0,.62); display:flex; align-items:center; justify-content:center; padding:24px; z-index:50; }
.modal{ width:520px; max-width:100%; background:var(--card); border:1px solid var(--line); border-radius:var(--r-card); box-shadow:var(--sh-pop); padding:26px 26px 24px; }
.modal__t{ display:flex; align-items:center; justify-content:center; gap:8px; text-align:center; font-size:13px; font-weight:700;
  letter-spacing:.06em; text-transform:uppercase; color:var(--blue); margin-bottom:20px; }
.modal__t svg{ width:16px; height:16px; }
.modal__l{ font-size:11px; font-weight:700; letter-spacing:.05em; text-transform:uppercase; color:var(--ink-3); margin:0 0 8px; }
.copyrow{ display:flex; align-items:center; gap:8px; background:var(--card-2); border:1px solid var(--line); border-radius:var(--r-ctl); padding:0 14px; height:48px; }
.copyrow .url{ flex:1; font-size:13.5px; color:var(--ink); overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
.modal__note{ font-size:12.5px; color:var(--ink-2); line-height:1.55; margin-top:14px; text-align:center; }
.modal__rows{ display:flex; flex-direction:column; gap:8px; margin-top:16px; }
.modal__rows .r{ display:flex; align-items:center; gap:10px; font-size:13px; color:var(--ink-2); }
.modal__rows.specrows .r{ justify-content:space-between; }
.modal__rows.specrows .r .k{ color:var(--ink-3); }
.minisel{ height:32px; border:1px solid var(--line); border-radius:8px; background:var(--card-2); padding:0 28px 0 11px; font:600 13px var(--f); color:var(--ink); cursor:pointer;
  appearance:none; -webkit-appearance:none;
  background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='8' viewBox='0 0 12 8'%3E%3Cpath d='M1 1l5 5 5-5' stroke='%23ffffff' stroke-opacity='.5' stroke-width='1.6' fill='none' stroke-linecap='round'/%3E%3C/svg%3E");
  background-repeat:no-repeat; background-position:right 9px center; }
.minisel:focus{ border-color:var(--blue); box-shadow:0 0 0 3px var(--blue-soft); outline:none; }
.miniinput{ height:32px; width:160px; border:1px solid var(--line); border-radius:8px; background:var(--card-2); padding:0 11px; font:600 13px var(--f); color:var(--ink); outline:none; }
.miniinput::placeholder{ color:rgba(255,255,255,.30); font-weight:500; }
.miniinput:focus{ border-color:var(--blue); box-shadow:0 0 0 3px var(--blue-soft); }
/* full-width name/message inputs in the link editor (claimed/just-made link) */
.modalinput{ width:100%; height:44px; border:1px solid var(--line); border-radius:var(--r-ctl); background:var(--card-2); padding:0 13px; color:var(--ink); font:500 14px var(--f); outline:none; margin-top:10px; }
.modalinput::placeholder{ color:rgba(255,255,255,.30); }
.modalinput:focus{ border-color:var(--blue); box-shadow:0 0 0 3px var(--blue-soft); }
.modal__actions{ display:flex; gap:12px; justify-content:center; margin-top:22px; }

/* usage meter, restored: bottom of the studio card, quiet tertiary, only shown near a cap */
.sv-usage{ flex:0 0 auto; text-align:center; font-size:11.5px; color:rgba(255,255,255,.38); padding:10px 0 2px; }

/* ---------- Account modal steps (profile image · log out everywhere · name) ---------- */
.acmodal{ width:360px; max-width:100%; background:#1C1C1E; border:1px solid rgba(255,255,255,.12); border-radius:16px;
  box-shadow:0 18px 50px rgba(20,24,40,.45); padding:22px 22px 20px; position:relative; }
.acmodal.confirm{ padding:24px 22px 20px; }
.acmodal__x{ position:absolute; top:14px; right:14px; width:28px; height:28px; border-radius:50%; border:none;
  background:rgba(255,255,255,.08); color:rgba(255,255,255,.6); display:flex; align-items:center; justify-content:center; cursor:pointer; }
.acmodal__x:hover{ background:rgba(255,255,255,.14); color:#fff; }
/* on the product step, not a literal (Sid, 2026-08-01). Was -.2px, which is -0.0125em at 16px and
   -0.0111em at the .confirm variant's 18px — the same declaration reading as two different
   trackings depending on which modal it was in, which is exactly what an em token fixes.
   Kept identical in studio.css and player.css: the modal markup is injected by acctmenu.js and the
   player copies these rules verbatim, so the two must move together. */
.acmodal h3{ margin:0; font-size:16px; font-weight:700; color:var(--ink); letter-spacing:var(--t-tracking); }
.acmodal.confirm h3{ font-size:18px; }
.acmodal .sub{ font-size:12.5px; color:var(--ink-2); margin-top:5px; line-height:1.5; }
.acmodal.confirm .sub{ font-size:13.5px; }
.acmodal .redicon{ width:46px; height:46px; border-radius:50%; background:var(--coral-soft); color:var(--coral);
  display:flex; align-items:center; justify-content:center; margin-bottom:14px; }
.acmodal .redicon svg{ width:22px; height:22px; }
/* same badge, asking rather than warning — "upload as" is a choice, not a danger */
.acmodal .blueicon{ width:46px; height:46px; border-radius:50%; background:rgba(59,116,240,.14); color:var(--blue);
  display:flex; align-items:center; justify-content:center; margin-bottom:14px; }
.acmodal .blueicon svg{ width:22px; height:22px; }
/* circular crop + zoom */
.acmodal .crop{ width:170px; height:170px; border-radius:50%; border:2px solid var(--blue); overflow:hidden;
  margin:18px auto 0; position:relative; background:#0c0c0e; touch-action:none; }
.acmodal .crop canvas{ display:block; width:100%; height:100%; cursor:grab; }
.acmodal .crop canvas:active{ cursor:grabbing; }
.acmodal .crop.empty{ display:flex; align-items:center; justify-content:center; cursor:pointer; }
.acmodal .crop .ph{ color:rgba(255,255,255,.45); font-size:12.5px; font-weight:500; }
.acmodal .zoom{ display:flex; align-items:center; gap:11px; margin:16px 2px 2px; color:rgba(255,255,255,.4); }
.acmodal .zoom svg{ width:15px; height:15px; flex:0 0 auto; }
.acmodal .zoom input[type=range]{ flex:1; -webkit-appearance:none; appearance:none; height:4px; border-radius:2px; background:rgba(255,255,255,.16); outline:none; }
.acmodal .zoom input[type=range]::-webkit-slider-thumb{ -webkit-appearance:none; width:16px; height:16px; border-radius:50%; background:#fff; cursor:pointer; box-shadow:0 1px 4px rgba(0,0,0,.5); }
.acmodal .zoom input[type=range]::-moz-range-thumb{ width:16px; height:16px; border:none; border-radius:50%; background:#fff; cursor:pointer; }
.acmodal .zoom input[type=range]:disabled{ opacity:.4; }
.acmodal .acinput{ width:100%; margin-top:16px; padding:12px 13px; border-radius:11px; border:1px solid rgba(255,255,255,.18);
  background:rgba(255,255,255,.07); color:var(--ink); font:500 14px var(--f); outline:none; }
.acmodal .acinput::placeholder{ color:rgba(255,255,255,.34); }
.acmodal .acinput:focus{ border-color:var(--blue); box-shadow:0 0 0 3px var(--blue-soft); }
.acmodal__row{ display:flex; gap:10px; margin-top:18px; }
.acbtn{ flex:1; display:flex; align-items:center; justify-content:center; text-align:center; gap:8px; padding:13px 12px;
  border-radius:10px; font:700 14px var(--f); cursor:pointer; border:none; transition:background .15s, filter .15s; }
.acbtn--sec{ background:transparent; color:var(--ink); border:1px solid rgba(255,255,255,.18); font-weight:600; }
.acbtn--sec:hover{ background:rgba(255,255,255,.06); }
.acbtn--blue{ background:var(--blue); color:#fff; }
.acbtn--blue:hover{ background:var(--blue-press); }
.acbtn--red{ background:var(--coral); color:#fff; }
.acbtn--red:hover{ filter:brightness(1.08); }
.acbtn:disabled{ opacity:.45; cursor:default; }

@media (max-width:480px){ .sheet{ padding:22px 18px 20px; } .fieldRow{ flex-direction:column; } .lhead{ flex-direction:column; gap:12px; } .usage{ text-align:left; min-width:0; width:100%; } }

/* ============================================================================
   UNIFIED OWNER PANEL — ONE component that morphs in place (Studio Manage design).
   Values verbatim from the bundle. Solid #1C1C1E card; the drop zone + waveform
   well are the only TRANSPARENT elements (a window to the wallpaper + a scrim).
   ============================================================================ */
/* --wall-img + the card chrome + .st-win frosted well + .st-wordmark are SHARED in sendbox.css
   (single source of truth — the homepage reuses these exact components). studio.css only adds the
   studio-specific shell (fixed height, internal scroll) on top. */
@keyframes vcsh{ 0%{transform:translateX(-100%)} 100%{transform:translateX(230%)} }
@keyframes vcpulse{ 0%,100%{transform:scale(1);opacity:.55} 50%{transform:scale(1.6);opacity:0} }

/* card SHELL only — the chrome (bg/border/radius/shadow/padding/width) is shared in sendbox.css (.st-card).
   The studio adds a fixed height + internal scroll so only the list scrolls; page/wallpaper never move. */
/* max-height uses svh (the STABLE small-viewport unit), NOT dvh: dvh changes when iOS Safari collapses the
   URL bar on keyboard focus and doesn't restore it, which grew the card and left it grown. svh is static
   across URL-bar + keyboard, so the card never resizes from typing — and svh always fits the visible viewport. */
/* the card WRAPS its content (ends just after the last row, the card padding is the comfortable
   bottom band); when the list outgrows the viewport it caps at max-height and the LIST scrolls
   within (.sv-list/.st-list are the flex-scrolling children) — the page itself never scrolls. */
/* overflow:CLIP, not hidden, and the difference is the whole point: hidden makes a scroll container
   that draws no bars but is still scrollable programmatically — which is exactly the handle WebKit
   grabs to chase a focused field, and it is what verify-ios-focus exists to catch. clip makes no
   scroll container at all. The action bar bleeds to this box's padding edge, so this is the element
   that has to be unable to scroll. (Same reasoning, same fix as the shared .st-win.) */
.st-card{ display:flex; flex-direction:column; overflow:clip; max-height:calc(100svh - 96px); text-transform:lowercase; }
/* card ground: sendbox.css owns it now — var(--surface-card) at the base, one surface family
   across homepage box, studio card and player (Sid's ruling, 2026-07-30) */

/* site-wide lowercase: OUR copy is lowercased by the card (and modals) above; everything the USER typed
   or that must keep its case is exempted here — track names, messages, account name/email, the link URL,
   the name input, and avatar initials. Keeps "people's studio files stay exactly as named". */
.acmodal{ text-transform:lowercase; }
.sv-rname, #svName, #title, #message, #acctName, #acctEmail, #urlText, #pmUrlMuted, #nameInput,
.st-avatar, .sb-acctpop .ava,
/* the wizard's receipt rows, for BOTH reasons this list exists: .st-sepName is what the sender
   typed, and .st-sepUrl carries a link token that is CASE-SENSITIVE — lowercasing it renders a
   dead link that still looks right (acetate.audio/a3tEbANo shown as .../a3tebano). */
.st-sepName, .st-sepUrl{ text-transform:none; }

/* short windows: reclaim the studio's vertical margin so the list keeps room (player/fields stay pinned; page never scrolls) */
@media (max-height:800px){
  .sb-stage{ padding-bottom:7px; }   /* the TOP is --card-top now, shared with every other surface */
  .st-card{ max-height:calc(100svh - 14px); }
}
/* one header for both views: ← · wordmark · avatar. wordmark stays left, avatar is anchored right with
   margin-left:auto, so the only change on the panel (Create/Edit) is the back button appearing on the left. */
.st-head{ flex:0 0 auto; display:flex; align-items:center; }
/* .st-wordmark — shared in sendbox.css */
.st-avatar{ flex:0 0 auto; margin-left:auto; width:30px; height:30px; border-radius:50%; background:var(--blue) center/cover no-repeat; color:#fff;
  display:flex; align-items:center; justify-content:center; font-size:13px; font-weight:600; border:none; cursor:pointer; }
.st-sectlabel{ flex:0 0 auto; font-family:var(--mono); font-size:11px; letter-spacing:0.2px; color:rgba(255,255,255,.5); }

/* .st-win frosted "window" (blurred wallpaper + scrim) — shared in sendbox.css */

/* drop zone (at rest) */
.st-drop{ flex:0 0 auto; border:2px dashed rgba(255,255,255,.22); border-radius:15px; padding:18px; margin-top:10px;
  display:flex; align-items:center; justify-content:center; gap:12px; cursor:pointer; transition:border-color .15s; }
.st-drop.drag{ border-color:var(--blue); }
.st-dropIcon{ flex:none; width:38px; height:38px; border-radius:50%; background:rgba(255,255,255,.07); display:flex; align-items:center; justify-content:center; color:#7fa0e8; }
.st-dropT{ font-weight:600; font-size:14.5px; color:#fff; }
.st-dropH{ font-size:12px; color:rgba(255,255,255,.5); margin-top:2px; }

/* "Drop another track" slim strip (panel open) */
.st-dropStrip{ flex:0 0 auto; display:flex; align-items:center; gap:9px; border:1.5px dashed rgba(255,255,255,.2); border-radius:11px;
  padding:9px 13px; margin-top:14px; cursor:pointer; transition:border-color .15s; }
.st-dropStrip.drag{ border-color:var(--blue); }
.st-dropStrip svg{ flex:none; color:#7fa0e8; }
.st-dropStrip span{ font-size:13px; font-weight:600; color:rgba(255,255,255,.78); }

/* player region — FIXED height so fields below never move as it morphs */
.st-player{ flex:0 0 auto; margin-top:12px; display:flex; flex-direction:column; justify-content:center; }
.st-pmFmt{ flex:0 0 auto; text-align:center; font-size:12.5px; color:rgba(255,255,255,.5); margin-top:8px; }   /* lossless · 44.1kHz / 16-bit */
/* overflow:CLIP, not hidden — the waveform canvas runs ~40px past the well, and a hidden box is
   still programmatically scrollable, so WebKit could scroll it to chase a caret exactly as it did
   the panel. Both axes are hidden here, so clip does not degrade to hidden the way it does beside
   an auto axis. Painting is unchanged; the clip-path already did that job. */
/* height comes from the shared component in sendbox.css. The create card was 110 while the panel
   was 120 — the studio's own two wells disagreed, so both are on the ruled height now. */
.st-well{ position:relative; border-radius:13px; clip-path:inset(0 round 13px); overflow:clip; cursor:pointer; }
.st-well.drag{ box-shadow:inset 0 0 0 2px var(--blue); }   /* drag-over feedback for the upload drop zone */
.st-well canvas{ position:absolute; inset:0; width:100%; height:100%; display:block; }
.st-wellPct{ position:absolute; left:50%; top:50%; transform:translate(-50%,-50%); font-family:var(--mono); font-size:12px; font-weight:600;
  color:#fff; background:rgba(0,0,0,.5); padding:3px 10px; border-radius:7px; white-space:nowrap; }
.st-wellHead{ position:absolute; top:0; bottom:0; width:2px; background:rgba(63,111,214,.75); }   /* upload fill edge */
.st-playhead{ position:absolute; top:7px; bottom:7px; width:2px; background:#fff; border-radius:2px; box-shadow:0 0 8px rgba(0,0,0,.6); }
.st-tL, .st-tR{ position:absolute; bottom:8px; font-family:var(--mono); font-size:11px; color:rgba(255,255,255,.78); background:rgba(0,0,0,.62); padding:2px 7px; border-radius:6px; }
.st-tL{ left:12px; } .st-tR{ right:12px; }
.st-playRow{ display:flex; justify-content:center; margin-top:12px; }
.st-play{ flex:none; width:80px; height:80px; border-radius:50%; border:none; background:var(--blue); box-shadow:0 16px 34px rgba(63,111,214,.55);
  cursor:pointer; display:flex; align-items:center; justify-content:center; padding:0; }
.st-play svg{ width:30px; height:30px; }
.st-play[disabled]{ background:rgba(255,255,255,.08); box-shadow:none; cursor:not-allowed; }

/* anchored fields — .st-fields/.st-input/.st-detailRow/.st-flabel/.st-pill/.st-expWrap/.st-menu/.st-pin/.st-go
   now live in the SHARED sendbox.css, so the studio uploader and the signed-out homepage box render from ONE
   source and the field type/spacing can't drift. (Moved 2026-06-26; values byte-identical, studio unchanged.) */

/* manage controls (morph in place of Get-your-link) */
.st-manage{ flex:0 0 auto; display:flex; align-items:flex-start; gap:9px; margin-top:12px; padding-bottom:10px; border-bottom:1px solid rgba(255,255,255,.1); }
.st-manageL{ flex:1; min-width:0; display:flex; flex-direction:column; gap:8px; }
.st-manageR{ flex:none; display:flex; flex-direction:column; gap:8px; }
.st-url{ display:flex; align-items:center; background:rgba(255,255,255,.07); border:1px solid rgba(255,255,255,.18); border-radius:11px; padding:9px 13px; }
.st-url .u{ flex:1; min-width:0; font-family:var(--mono); font-size:13px; color:rgba(255,255,255,.78); white-space:nowrap; overflow:hidden; text-overflow:ellipsis; }
.st-url .ok{ display:none; align-items:center; gap:4px; font-size:11px; font-weight:600; color:#30D158; white-space:nowrap; margin-left:8px; }
.st-url.copied .ok{ display:inline-flex; }
.st-del{ align-self:flex-start; display:inline-flex; align-items:center; gap:6px; background:none; border:none; cursor:pointer; padding:4px 2px; font-family:inherit; }
.st-del span{ font-size:11px; font-weight:600; color:#FF453A; }
.st-mbtn{ display:flex; align-items:center; justify-content:center; gap:7px; padding:9px 18px; border-radius:11px; cursor:pointer; font:600 14.5px var(--f); border:none; }
.st-mbtn.copy{ background:var(--blue); color:#fff; box-shadow:0 8px 18px rgba(63,111,214,.30); }
.st-mbtn.share{ background:transparent; color:#fff; border:1px solid rgba(255,255,255,.18); }
/* `.demoted` is DELETED, not left lying about: with the bar there is one primary slot and copy+share
   step out of it entirely while a save is pending, so a demoted copy button no longer exists. */

/* the list — flexes to fill, scrolls inside the card (page never scrolls) */
.st-list{ flex:1 1 auto; min-height:0; overflow-y:auto; margin-top:2px; padding:2px 0; }
.st-list.strip{ padding:6px 0;
  -webkit-mask-image:linear-gradient(180deg,transparent 0,#000 18px,#000 calc(100% - 18px),transparent 100%);
  mask-image:linear-gradient(180deg,transparent 0,#000 18px,#000 calc(100% - 18px),transparent 100%); }
/* NO custom scrollbar on the studio list — the reference styles it NOWHERE (zero scrollbar rules,
   measured headless), which is what buys its behaviour: the OS overlay bar, hidden at rest,
   appearing only while scrolling, in the system's dark subtle style. Any ::-webkit-scrollbar rule
   is what DEFEATS that in Chrome and Safari alike — it switches the element to always-visible
   custom bars, which is exactly the standing light-grey bar Sid flagged. Matching the reference
   here means deleting, not restyling. (The panel tracklist's hidden-scrollbar rule is separate
   and deliberate: its fade is the affordance.) */
.st-lrow{ display:flex; align-items:center; gap:11px; padding:9px 0; border-bottom:1px solid rgba(255,255,255,.1); }
.st-lrow:last-child{ border-bottom:none; }
.st-lplay{ flex:none; width:32px; height:32px; border-radius:50%; border:none; background:var(--blue); cursor:pointer; display:flex; align-items:center; justify-content:center; padding:0; }
.st-lplay svg{ width:12px; height:12px; }
.st-linfo{ flex:1; min-width:0; }
.st-lname{ font-weight:600; font-size:13.5px; white-space:nowrap; overflow:hidden; text-overflow:ellipsis; color:#fff; cursor:pointer; }
.st-lmeta{ font-size:11.5px; color:rgba(255,255,255,.55); margin-top:1px; }
.st-lmeta .x{ font-weight:600; }   /* time-left, coloured by the expiry scale */
.st-lkebab{ flex:none; border:none; background:none; cursor:pointer; padding:5px; display:flex; }
.st-empty{ font-size:13px; color:rgba(255,255,255,.45); text-align:center; padding:18px 0; }
.st-foot{ flex:0 0 auto; text-align:center; margin-top:16px; padding-top:12px; border-top:1px solid rgba(255,255,255,.1); }
.st-foot div{ font-size:11px; color:rgba(255,255,255,.45); font-weight:500; }

@media (max-width:480px){
  .st-card{ width:100%; max-width:440px; max-height:calc(100svh - 16px - env(safe-area-inset-top,0px)); padding:18px 20px; }
  .st-player{ margin-top:10px; }
  /* .st-detailRow/.st-flabel/.st-pill/.st-pin mobile sizing moved to the shared sendbox.css (one source) */
}

/* ====================== STUDIO RESTRUCTURE — views ====================== */
/* the visible view fills the card; its list is the only internal scroller */
#viewStudio, #viewPanel{ display:flex; flex-direction:column; flex:1 1 auto; min-height:0; }
#viewPanel{ overflow-y:auto; }   /* create/edit/ready scroll within the capped card instead of overflowing the window */
.st-back{ display:inline-flex; align-items:center; gap:5px; margin-right:13px; border:none; background:none; cursor:pointer; padding:0;
  font:600 13px var(--f); color:rgba(255,255,255,.7); }

/* upload-another strip (→ Create) */
.sv-upload{ flex:0 0 auto; display:flex; align-items:center; gap:9px; margin-top:16px; padding:12px 13px; cursor:pointer;   /* ref strip is 40 tall; 12px vertical (--sp-close's value) lands 41 */
  border:none; border-radius:11px; clip-path:inset(0 round 11px); background:transparent; font:inherit; color:inherit; text-align:left;
  outline:1.5px dashed rgba(255,255,255,.2); outline-offset:-1.5px; }   /* dashed via OUTLINE not border: a real border + blurred .st-win ::before defeats the rounded-corner clip on iOS */
.sv-upload svg{ flex:none; }
.sv-upload span{ font-size:13px; font-weight:600; color:rgba(255,255,255,.78); }
.sv-upload .chev{ margin-left:auto; }
.sv-upload:hover{ border-color:rgba(255,255,255,.34); }

/* loaded-track waveform well */
 /* PREVIEW REGION to the reference (2026-07-30): the overlay showed live's whole preview +
   transport block ~49px shorter, so every list row ghosted high. Measured: well 120 (was 102, a
   component dimension like the play button's 84), strip->well and well->title 18 (--sp-block, was
   12), spec->play and play->filter on --sp-air (was 14px literals). The phone keeps its own block
   (well 84 and the smaller step values) — the reference is desktop-only. */
.sv-well{ position:relative; flex:0 0 auto; height:120px; border-radius:13px; clip-path:inset(0 round 13px); overflow:hidden; cursor:pointer; margin-top:var(--sp-block); }
.sv-well canvas{ position:absolute; inset:0; width:100%; height:100%; display:block; }
.sv-playhead{ position:absolute; top:7px; bottom:7px; width:2px; background:#fff; border-radius:2px; box-shadow:0 0 8px rgba(0,0,0,.6); }
.sv-tL, .sv-tR{ position:absolute; bottom:8px; font-family:var(--mono); font-size:11px; color:rgba(255,255,255,.78); background:rgba(0,0,0,.62); padding:2px 7px; border-radius:6px; }
.sv-tL{ left:12px; } .sv-tR{ right:12px; }
.sv-wellMsg{ position:absolute; left:50%; top:50%; transform:translate(-50%,-50%); font-family:var(--mono); font-size:11.5px;
  color:rgba(255,255,255,.7); background:rgba(0,0,0,.5); padding:4px 11px; border-radius:7px; white-space:nowrap; }

/* loaded-track label */
.sv-loaded{ flex:0 0 auto; text-align:center; margin-top:var(--sp-block); }
.sv-loadedName{ font-weight:600; font-size:14px; color:#fff; white-space:nowrap; overflow:hidden; text-overflow:ellipsis; cursor:pointer; }   /* tap to reveal in full (.expanded) */
.sv-loadedFmt{ font-size:11.5px; color:rgba(255,255,255,.5); margin-top:1px; }

/* transport row */
.sv-transport{ position:relative; flex:0 0 auto; display:flex; align-items:center; justify-content:center; gap:14px; margin-top:var(--sp-air); min-height:84px; }   /* min-height = the 84 play button itself: the old 88 left 2px of slack that quietly fudged BOTH gaps around the transport */
.sv-tbtn{ flex:none; width:50px; height:50px; border-radius:50%; border:none; background:#2C2C2E; cursor:pointer; display:flex; align-items:center; justify-content:center; padding:0; }
.sv-tbtn:disabled{ opacity:.4; cursor:not-allowed; }
.sv-play{ flex:none; width:88px; height:88px; border-radius:50%; border:none; background:var(--blue); box-shadow:0 16px 34px rgba(63,111,214,.55);
  cursor:pointer; display:flex; align-items:center; justify-content:center; padding:0; }
.sv-play:disabled{ background:rgba(255,255,255,.12); box-shadow:none; cursor:not-allowed; }
/* (moved to sendbox.css — ONE shared control, see the SHARED CONTROLS block there) */
/* the downloads row reuses the sv-toggle switch; quiet tertiary note beside it. NO margin
   override: the .st-detailRow family rule (sendbox.css) supplies the same 9px gap as every
   other field row, and its flex centring aligns the toggle + note on the label. */
.st-dlRow .sv-toggle{ flex:none; width:26px; height:16px; cursor:pointer; }
.st-dlRow .sv-toggle .knob{ width:12px; height:12px; }
.st-dlNote{ font-size:11.5px; color:rgba(255,255,255,.38); }
.st-dlNote.on{ color:var(--blue-text); }
.sv-toggle{ display:inline-flex; align-items:center; width:20px; height:12px; border-radius:999px; background:rgba(255,255,255,.2);
  padding:1.5px; cursor:pointer; border:none; justify-content:flex-start; transition:background .15s,justify-content 0s; }
.sv-toggle .knob{ width:9px; height:9px; border-radius:50%; background:#fff; }
.sv-toggle[aria-checked="true"]{ background:#30D158; justify-content:flex-end; }
.sv-toggle[aria-checked="true"] .knob{ box-shadow:0 1px 2px rgba(0,0,0,.4); }
/* (moved to sendbox.css — ONE shared control, see the SHARED CONTROLS block there) */

/* YOUR TRACKS header + sort */
.sv-tracksHead{ position:relative; flex:0 0 auto; display:flex; align-items:center; justify-content:space-between; margin-top:var(--sp-air); min-height:18px; }
.sv-headR{ display:inline-flex; align-items:center; gap:12px; }
.sv-sortWrap{ position:relative; }
.sv-sortBtn{ display:inline-flex; align-items:center; gap:6px; border:1px solid rgba(255,255,255,.18); background:rgba(255,255,255,.07);
  border-radius:8px; padding:5px 8px; cursor:pointer; font:inherit; }
.sv-sortBtn.open{ border-color:var(--blue); background:rgba(59,116,240,.14); }
.sv-sortTxt{ font-size:11.5px; font-weight:600; color:rgba(255,255,255,.78); }
.sv-sortChev{ transition:transform .15s; }
.sv-sortBtn.open .sv-sortChev{ transform:rotate(180deg); }
.sv-sortMenu{ position:absolute; top:calc(100% + 8px); right:0; width:188px; background:#1C1C1E; border:.5px solid rgba(255,255,255,.2);
  border-radius:12px; box-shadow:0 18px 44px rgba(0,0,0,.6); padding:5px; z-index:40; }
.sv-sortMenu .mi{ display:flex; align-items:center; justify-content:space-between; padding:9px 11px; border-radius:8px; font-size:13px; color:#fff; cursor:pointer; }
.sv-sortMenu .mi:hover{ background:rgba(255,255,255,.06); }
.sv-sortMenu .mi.on{ background:rgba(59,116,240,.18); font-weight:600; }
.sv-sortMenu .mi svg{ opacity:0; }
.sv-sortMenu .mi.on svg{ opacity:1; }

/* the queue — the only scroller (page/wallpaper never move) */
.sv-list{ flex:1 1 auto; min-height:0; overflow-y:auto; overflow-x:hidden; margin-top:4px; padding:2px 0;
  -webkit-mask-image:linear-gradient(180deg,transparent 0,#000 14px,#000 calc(100% - 14px),transparent 100%);
  mask-image:linear-gradient(180deg,transparent 0,#000 14px,#000 calc(100% - 14px),transparent 100%); }
/* AIRY RULING EXTENDED TO THE TOP LEVEL (Sid, 2026-07-30). The reference's rows carry 12px above
   and below the content — the gap either side of each row's bottom line — where live had 9.
   12px FLAT AT BOTH WIDTHS, a component dimension like the sub-rows' own 10px slack: the ruling
   says "same slack behaviour as the sub-rows", and the sub-row family holds its vertical slack
   flat on phones (only its horizontal follows the phone scale). The first cut put this on
   var(--sp-close), which is 9 at 390 — top rows compressed while their own sub-rows did not, and
   the harness passed because it asserted that choice rather than the ruling. Padding-based, so a
   one-line and a two-line collection title carry identical slack and read as one family. Type
   sizes stay on their snapped steps; only the room changes. */
/* The LEFT INSET is what makes the divider read long (Sid, 2026-07-31). Measured, the dividers
   themselves were already identical — ref and live both span 1 -> 367 of the card's content box,
   366 wide. What differed is the content: the reference insets a row's content 12px, so the line
   runs visibly past the drag dots at both ends; live's content started level with the line, so the
   line looked to begin under the dots and therefore short. Same pixels of line, different reading.
   --sp-close, so the phone keeps its own step. It also lands the dl stripe where the reference
   draws it — see .sv-row.dl::before. */
.sv-row{ position:relative; display:flex; align-items:center; gap:11px;
  padding:12px var(--sp-tight) 12px var(--sp-close); border-bottom:1px solid var(--row-line); }
/* downloads-on indicator: a thin primary-blue stripe on the row's LEFT EDGE, drawn in the card's
   padding gutter (absolute, negative left) — zero horizontal content cost, clear of the drag
   handle in custom-order mode, holds at 320px (the card keeps >=14px side padding everywhere). */
/* FULL height, divider to divider (Sid's ruling, 2026-07-30): the row's box runs exactly line to
   line, so top/bottom 0 is the reference's "runs tight". The 6px insets were making it float with
   gaps either side. NB adopted from the worded ruling — neither reference HTML renders a
   downloads row to measure, and that is stated rather than papered over. Blue stays the RULED
   shared --blue. */
/* DIVIDER TO DIVIDER, meant literally. Absolute offsets resolve against the PADDING box, so
   top:0/bottom:0 stopped 1px short of the row's border box — exactly the row's own divider, left
   uncovered — and the 2px radius on a 3px bar rounded the last pixels away from both lines on top
   of that. bottom:-1px reaches the border box; square ends meet the lines instead of curving off
   them. Measured against live's own painted divider positions: neither reference export renders a
   downloads row, so the row's real bounds are the anchor. */
/* WIDTH FROM THE REFERENCE, which does render this: not as a bar in the gutter but as a 2px
   border-left on the row itself (rgb(47,107,250)) — invisible to a pseudo-element scan, which is
   why the first two attempts had nothing to measure and worked from the wording instead.
   Adopted here: 2px, and vertical bounds that span the row's BORDER box, which is what a
   border-left spans and is therefore divider to divider by construction.
   PLACEMENT, resolved by the row inset above rather than flagged: the reference's stripe sits ON
   the row's left edge, where the divider lines begin, so line/stripe/line read as one connected
   bracket. Live's floated 9px out in the gutter and the three never touched. The blocker was that
   moving it in would put it under the drag dots — but the 12px content inset moves the dots clear,
   so left:-(--sp-close) now lands the stripe exactly on the row's border-box edge, which is where
   a border-left paints. Same result as the reference's mechanism, without the layout shift a real
   border-left would impose on dl rows only. */
/* left:0 — the row's own left edge, where the divider lines begin. An absolute offset resolves
   against the containing block's PADDING BOX EDGE; it is not additionally shifted by padding-left.
   left:-(--sp-close) therefore did NOT cancel the row's new inset, it pushed the stripe 11px out
   into the gutter, which is where it had been all along. Caught by the overlay, not by the
   harness: my check computed the painted x with the same wrong arithmetic and passed. */
.sv-row.dl::before{ content:''; position:absolute; left:0; top:0; bottom:-1px; width:2px; border-radius:0; background:var(--blue); }
/* the list is a scroll container, so it CLIPS at its own box — widen it leftward with a
   compensating negative margin (content doesn't move a pixel) so the -9px stripe isn't cut off */
.sv-list{ padding-left:12px; margin-left:-12px; }
.sv-row.playing{ background:#2C2C2E; border-radius:8px; border-bottom-color:transparent; }   /* the active/playing track (surface-2) */
/* tap a truncated title to reveal it in full. overflow-wrap:anywhere lets an UNBROKEN token (a long
   filename like Artist_Track_2448_Mix_1) break mid-word and wrap DOWN — without it white-space:normal
   has no break points, the text pours out sideways, and the list grows a horizontal scrollbar. */
.sv-rname.expanded, .sv-loadedName.expanded{ white-space:normal; overflow:visible; overflow-wrap:anywhere; word-break:break-word; }
.sv-row:last-child{ border-bottom:none; }
.sv-row.dragging{ opacity:.4; }
.sv-row.liftrow{ background:#2C2C2E; box-shadow:0 18px 36px rgba(0,0,0,.6); border:1px solid rgba(59,116,240,.5); border-radius:12px; padding:10px 12px; }
/* (base .sv-drag retired — the shared handle rule further down owns both levels) */
.sv-rplay{ flex:none; width:30px; height:30px; border-radius:50%; border:none; background:var(--blue); cursor:pointer; display:flex; align-items:center; justify-content:center; padding:0; }
.sv-rinfo{ flex:1; min-width:0; }
/* rows: truncate with ellipsis; TAP the title to reveal it in full (the established list
   behaviour). The expanded state uses anywhere-breaking because plugin-created takes are single
   UNBROKEN underscore strings ('MattJones_CouldBeSomething_2448_Mix_1_…' — zero spaces). */
.sv-rname{ font-weight:600; font-size:13.5px; white-space:nowrap; overflow:hidden; text-overflow:ellipsis; color:#fff; cursor:pointer; }
/* the title -> meta breath. The reference's 5.38px is measured between GLYPH boxes; live's title
   and meta are blocks, so their boxes are LINE boxes and already carry the half-leading of the
   declared line-heights above. Applying 5.38 as a margin therefore counted that leading twice and
   pushed the one-line row to 66 against the reference's 63. 4px is the line-box equivalent: it
   reproduces the reference's visible 5.38 between the glyphs and its 63px row.
   Same box-vs-ink error as the sub-row gaps, in the vertical axis — a measurement is only
   transferable between two documents if it is taken from the same kind of edge in both. */
.sv-rmeta{ font-size:11.5px; color:rgba(255,255,255,.55); margin-top:4px; }
.sv-rmeta .x{ font-weight:600; }
.sv-rkwrap{ position:relative; flex:none; }
.sv-rkebab{ border:none; background:none; cursor:pointer; padding:5px; display:flex; }
.sv-rmenu{ position:absolute; top:calc(100% + 4px); right:0; width:128px; background:#1C1C1E; border:.5px solid rgba(255,255,255,.2);
  border-radius:11px; box-shadow:0 18px 40px rgba(0,0,0,.6); padding:5px; z-index:30; }
.sv-rmenu button{ display:block; width:100%; text-align:left; border:none; background:none; cursor:pointer; font:500 13px var(--f);
  color:#fff; padding:8px 10px; border-radius:7px; }
.sv-rmenu button:hover{ background:rgba(255,255,255,.06); }

@media (max-width:480px){
  .sv-well{ height:84px; }
  .sv-transport{ min-height:72px; gap:12px; }
  .sv-tbtn{ width:44px; height:44px; }
  .sv-play{ width:72px; height:72px; }
}

/* ====================== CREATE / EDIT / LINK-READY ====================== */
.st-card.auto{ height:auto; }   /* Create/Edit/Ready size to content; the Studio view keeps the fixed 800 */
.st-mutedUrl{ flex:0 0 auto; font-family:var(--mono); font-size:11.5px; color:rgba(255,255,255,.4); margin-top:14px; white-space:nowrap; overflow:hidden; text-overflow:ellipsis; }
.st-shareRow{ display:flex; align-items:center; justify-content:space-between; gap:12px; margin-top:14px; }
.st-shareTxt{ min-width:0; padding-right:12px; }
.st-shareT{ font-size:13.5px; font-weight:500; color:#fff; }
.st-shareH{ font-size:11.5px; color:rgba(255,255,255,.5); margin-top:1px; }
.st-sw{ flex:none; display:inline-flex; align-items:center; width:42px; height:25px; border-radius:999px; background:rgba(255,255,255,.18); padding:2.5px; cursor:pointer; border:none; justify-content:flex-start; transition:background .15s; }
.st-sw .knob{ width:20px; height:20px; border-radius:50%; background:#fff; }
.st-sw[aria-checked="true"]{ background:#30D158; justify-content:flex-end; }
.st-sw[aria-checked="true"] .knob{ box-shadow:0 1px 3px rgba(0,0,0,.4); }
.st-delLink{ display:inline-flex; align-items:center; gap:6px; border:none; background:none; cursor:pointer; padding:0; font:600 12.5px var(--f); color:#FF453A; }
/* link-ready beat */
#pmReady{ flex:0 0 auto; display:flex; flex-direction:column; }
.st-readyCheck{ width:54px; height:54px; border-radius:50%; background:rgba(48,209,88,.14); display:flex; align-items:center; justify-content:center; margin:22px auto 0; }
.st-readyTitle{ text-align:center; font-weight:700; font-size:19px; letter-spacing:-.3px; color:#fff; margin-top:14px; }
.st-readyUrl{ margin-top:9px; padding:8px 13px; }   /* slim to match the name/message field height */
/* post-save confirmation: green tick + "saved" on a transparent button (green = success, never a green fill) */
#saveBtn.saved{ background:transparent; box-shadow:none; border:1px solid rgba(48,209,88,.45); color:#30D158; }
.st-readyCue{ text-align:center; font-size:12px; color:rgba(255,255,255,.45); margin-top:8px; }

/* mobile gesture scoping: the well is the scrubber (horizontal drag, no browser gesture);
   the lists keep normal vertical scroll but never bubble overscroll to the page (no pull-to-refresh) */
.sv-well, .st-well{ touch-action:none; }
.sv-list, .st-list{ overscroll-behavior:contain; }

/* MOBILE stage: the card spans nearly the full viewport (14px side margins — the desktop 56px
   gutters made it feel shrunken); bottom keeps the need-help pill clearance */
@media (max-width:480px){
  .sb-stage{ padding:var(--card-top) 14px max(64px, calc(env(safe-area-inset-bottom) + 50px)); }
}

/* ============ MASTER LINK — panel tracklist (dh §1) ============================================
   The stack's running order, sitting where a single link shows just its one track. Rows are quiet
   furniture on solid surface-1 (GLOBAL card rule: list rows never go glassy); the waveform well
   above stays the hero. Drag handle + × mirror the studio list's affordances so nothing new has to
   be learned. Numbers/durations are Roboto Mono per the type rule. */
.st-tracks{ margin:2px 0 14px; }
.st-tkHead{ display:flex; align-items:baseline; justify-content:space-between; margin-bottom:6px; }
.st-tkCount{ font:500 11.5px/1 'Roboto Mono',monospace; letter-spacing:.04em; color:rgba(255,255,255,.38); }
.st-tkAdd{ background:none; border:0; padding:0; cursor:pointer; font:600 12px Inter,sans-serif; color:var(--blue-text); }
.st-tkAdd:hover{ text-decoration:underline; }
.st-tkAdd[disabled]{ color:rgba(255,255,255,.28); cursor:default; text-decoration:none; }
.st-tkList{ display:flex; flex-direction:column; }
.st-tkRow{ display:flex; align-items:center; gap:9px; padding:7px 0; border-bottom:1px solid rgba(255,255,255,.1); }
.st-tkRow:last-child{ border-bottom:none; }
.st-tkRow.dragging{ opacity:.4; }
.st-tkDrag{ flex:none; cursor:grab; display:flex; padding:1px; touch-action:none; }
.st-tkDrag:active{ cursor:grabbing; }
.st-tkNum{ flex:none; width:15px; font:500 11px/1 'Roboto Mono',monospace; color:rgba(255,255,255,.3); }
.st-tkName{ flex:1; min-width:0; font:600 13px Inter,sans-serif; color:#fff; white-space:nowrap; overflow:hidden; text-overflow:ellipsis; }
.st-tkDur{ flex:none; font:400 11px/1 'Roboto Mono',monospace; color:rgba(255,255,255,.4); }
.st-tkX{ flex:none; background:none; border:0; padding:2px; cursor:pointer; display:flex; border-radius:6px; }
.st-tkX:hover{ background:rgba(255,255,255,.07); }
/* uploading rows: the per-track progress the sequential queue writes into */
.st-tkRow.pending .st-tkName{ color:rgba(255,255,255,.5); }
.st-tkRow.pending .st-tkDur{ color:var(--blue-text); }
.st-tkNote{ margin-top:8px; font-size:11.5px; line-height:1.45; color:rgba(255,255,255,.38); }
.st-tkNote.warn{ color:#FF9F0A; }
/* add-source sheet */
.st-tkPick{ margin-top:9px; padding:10px 11px; background:#2C2C2E; border-radius:11px; }
.st-tkPickFile{ width:100%; background:none; border:1px dashed rgba(255,255,255,.22); border-radius:9px;
  padding:9px; cursor:pointer; font:600 12.5px Inter,sans-serif; color:#fff; }
.st-tkPickFile:hover{ border-color:var(--blue); }
.st-tkPickHead{ margin:11px 0 5px; font:500 11px/1 'Roboto Mono',monospace; color:rgba(255,255,255,.32); }
/* A SCROLL LANE, not a styled scrollbar — the delete-scrollbar-css ruling stands and nothing here
   touches ::-webkit-scrollbar. These rows are CLICK TARGETS, which is what makes this list
   different from the read-only ones: measured live, the list scrolls (269px of rows in 190px), the
   bar is an OVERLAY that paints over content, and the rows cleared a 0px lane — so on a machine set
   to "always show scroll bars", where the bar is opaque and hit-testable, the bar sits on top of
   every row's right edge and takes the click. That is Sid's "clicks land on or under the bar".
   15px is the widest classic macOS bar; an overlay bar is narrower, so clearing 15 clears both.
   Trade-off, stated: where the bar IS classic the browser already insets the content box, so the
   rows end up 30px in rather than 15. Safe in both directions, which is the point — the alternative
   (scrollbar-gutter:stable) is a no-op under overlay bars and would leave the reported bug alone. */
.st-tkPickList{ max-height:190px; overflow-y:auto; padding-right:15px; }
.st-tkPickRow{ display:flex; align-items:center; gap:9px; padding:7px 2px; cursor:pointer;
  border-bottom:1px solid rgba(255,255,255,.07); }
.st-tkPickRow:last-child{ border-bottom:none; }
.st-tkPickRow .nm{ flex:1; min-width:0; font:500 12.5px Inter,sans-serif; color:#fff; white-space:nowrap; overflow:hidden; text-overflow:ellipsis; }
.st-tkPickRow .du{ flex:none; font:400 10.5px/1 'Roboto Mono',monospace; color:rgba(255,255,255,.4); }
/* how many copies of this track the collection holds. Sits WITH the name, not with the tick: it is
   a fact about the running order, and the tick is a fact about the row. */
.st-tkPickRow .x2{ flex:none; font:600 10.5px/1 'Roboto Mono',monospace; color:var(--blue-text); }
.st-tkPickRow .tick{ flex:none; width:15px; height:15px; display:flex; align-items:center; justify-content:center; opacity:0; }
.st-tkPickRow.added .tick{ opacity:1; }
.st-tkPickRow.added .nm{ color:rgba(255,255,255,.45); }
/* the optimistic tick is already painted, so this must NOT dim the row — it only stops a second tap
   landing on a round trip that has not answered yet (which is how you get two copies from one
   intention). No opacity, no spinner: the row is supposed to look done, because it almost is. */
.st-tkPickRow.pending{ pointer-events:none; }
.st-tkPickEmpty{ padding:6px 2px; font-size:11.5px; color:rgba(255,255,255,.32); }
/* master links can't burn — the expiry menu drops "after 1 play" and this says why */
.st-burnNote{ margin-top:7px; font-size:11.5px; color:rgba(255,255,255,.38); }

/* ============ MASTER LINK — the studio stack row (dh §2) =======================================
   One row in Your Tracks: title + "N tracks · X days left". The chevron alone expands (tapping the
   row keeps doing what every other row does); the versions appear as indented sub-rows carrying a
   name, a duration and a reorder handle — and NOTHING else: the envelope owns the rules, so
   sub-rows have no play, no menu, no download of their own. */
.sv-chev{ flex:none; background:none; border:0; padding:3px; cursor:pointer; display:flex; border-radius:6px; transition:transform .18s ease; }
.sv-chev:hover{ background:rgba(255,255,255,.07); }
.sv-row.open .sv-chev{ transform:rotate(180deg); }
.sv-sub{ padding:2px 0 8px 34px; border-bottom:1px solid rgba(255,255,255,.1); }
.sv-sub[hidden]{ display:none; }
.sv-subRow{ display:flex; align-items:center; gap:9px; padding:5px 0; }
.sv-subRow.dragging{ opacity:.4; }
/* THE HANDLE, one rule for both levels: 2px dot pairs (2 cols x 3 rows, 5px pitch) at half
   strength, the reference's treatment measured at top level AND inside the panel, where it is
   identical.
   The 6px padding rebuilds the 19x24 HIT AREA around the 7x12 dot field, so the grab target never
   shrank to the dots' own size; the negative margin cancels that padding FOR LAYOUT ONLY. Measured
   ink-edge to ink-edge, the padding was pushing the dots 6px in from the row's own padding edge and
   opening the dots->circle gap to 18 against the reference's 12. The hit area is unchanged — it
   simply overhangs into the row padding, where there is nothing to collide with. Box edges said 12
   and looked wrong; ink edges said 18 and were right. */
/* -webkit-user-select + touch-callout: on iOS a long press near text starts a SELECTION, so
   reaching for the dots highlighted the track title instead of grabbing the handle. touch-action
   already stopped the scroll gesture; it does nothing about selection. The dragging class extends
   the same suppression to the row for the length of the drag, because the finger drifts off the
   7x12 dot field almost immediately. */
.sv-drag, .sv-subDrag, .st-tkDrag{ flex:none; cursor:grab; display:flex; padding:6px; margin:-6px; opacity:.5;
  touch-action:none; -webkit-user-select:none; user-select:none; -webkit-touch-callout:none; }
.sv-row.dragging, .sv-subRow.dragging, .st-tkRow.dragging,
.sv-row.dragging *, .sv-subRow.dragging *, .st-tkRow.dragging *{
  -webkit-user-select:none; user-select:none; -webkit-touch-callout:none; }
.sv-drag:active, .sv-subDrag:active, .st-tkDrag:active{ cursor:grabbing; }
/* RESERVED, RIGHT-ALIGNED, and no wider than it needs to be (Sid, 2026-07-30). The column stays
   reserved so titles keep one left edge at ten-plus tracks — that ruling stands — but the numeral
   now sits at its RIGHT edge, snug against the gap to the title, which is what the reference's
   hugging column does visibly. 2ch with tabular figures IS the two-digit width exactly, so the
   old fixed 14px (3.4px wider than "15" needs) stops donating slack to the gap, and the digits
   column-align down the list as a bonus. See the width note in the type block below. */
.sv-subNum{ flex:none; font:500 10.5px/1 'Roboto Mono',monospace; color:rgba(255,255,255,.28);
  /* AFTER the shorthand, always: `font:` resets font-variant, so declaring tabular figures above
     it silently does nothing. `2ch` resolves against the element's FINAL computed font (Inter at
     --t-caption, set further down), not this line's. */
  /* Reserved from the collection's OWN largest number (--num-ch, set per panel in studio.js),
     not from the 15-track cap. Reservation exists so titles share one left edge WITHIN a list, and
     the width that achieves that is the widest number IN that list — a flat 2ch made every
     single-digit collection donate a digit of slack to the circle->number gap for a two-digit case
     it does not have. 3 tracks: 1ch, and both gaps measure 12 like the reference. 12 tracks: 2ch,
     titles align, and the gaps stay 12 because the ink now fills the column. */
  width:calc(var(--num-ch, 2) * 1ch); text-align:right; font-variant-numeric:tabular-nums; }
.sv-subName{ flex:1; min-width:0; font:500 12.5px Inter,sans-serif; color:rgba(255,255,255,.82);
  white-space:nowrap; overflow:hidden; text-overflow:ellipsis; }
.sv-subDur{ flex:none; font:400 10.5px/1 'Roboto Mono',monospace; color:rgba(255,255,255,.35); }
/* the blue release stripe belongs to the ENVELOPE, so it runs the stack row + its expansion */
.sv-row.dl + .sv-sub{ position:relative; }
/* the envelope's release stripe runs to the SAME divider the row stripe now runs to (the ruled
   "runs tight, divider to divider"); with margin-top 0 above, it joins the row's stripe seamlessly.
   Inferred from the row ruling — neither reference export renders a downloads row. */
.sv-row.dl + .sv-sub::before{ content:''; position:absolute; left:0; top:0; bottom:-12px; width:2px; border-radius:0; background:var(--blue); }

/* whole-surface drop target (bug: a file dropped off the small well made the BROWSER open the file
   and the studio vanished). The card lights its edge so it's obvious the drop will land. */
#viewPanel.dropping, #viewStudio.dropping{ position:relative; }
#viewPanel.dropping::after, #viewStudio.dropping::after{
  content:''; position:absolute; inset:-6px; border:1.5px dashed var(--blue); border-radius:16px;
  background:rgba(59,116,240,.06); pointer-events:none; z-index:5; }

/* ---- the link-cap door ----------------------------------------------------------------------
   The capped strip takes "upload another track"'s slot and window finish, so the card's shape
   holds; only the meaning changes. The message is quiet caption ink — it is information, not an
   alarm — and the waitlist action is smaller still: the primary move on a full account is
   deleting a link, and the visual weight says so. */
.sv-capNote{ flex:0 0 auto; margin-top:16px; padding:10px 13px; border-radius:11px; clip-path:inset(0 round 11px); }
.sv-capMsg{ font:500 var(--t-caption)/1.45 Inter,sans-serif; color:rgba(255,255,255,.6); }
.sv-capAction{ margin-top:6px; }
.st-capNotify{ background:none; border:0; padding:0; cursor:pointer; font:600 var(--t-mono)/1.4 Inter,sans-serif;
  color:rgba(255,255,255,.45); text-decoration:underline; text-underline-offset:2px; }
.st-capNotify:hover{ color:rgba(255,255,255,.65); }
/* tapped: a quiet receipt, not a control — no underline, no hover invitation */
.st-capNotify[disabled]{ cursor:default; text-decoration:none; color:rgba(255,255,255,.4); }
/* the panel's cap note hosts the same action inline after the message */
.st-tkNote .st-capNotify{ margin-left:4px; }

/* ---- separate-links wizard: the step marker + the done receipt ------------------------------
   The marker takes the SAME quiet blue caption treatment as .st-namePrompt — both say "there is
   one thing to do on this screen", so they read as one voice rather than two. The receipt's rows
   reuse .st-url's finish (the same well the copy-link box sits in) so a list of links looks like
   the link box the sender already knows. */
.st-sepStep{ flex:0 0 auto; font:600 11.5px/1 Inter,sans-serif; color:var(--blue-text); margin:0 0 10px 2px; }
#pmSepDone{ flex:0 0 auto; display:flex; flex-direction:column; }
.st-sepDoneTitle{ font:700 var(--t-title)/1.15 Inter,sans-serif; letter-spacing:var(--t-title-ls);
  color:#fff; margin:4px 0 var(--sp-block); }
/* NO second scroller: #viewPanel already scrolls inside the capped card, which is the panel's own
   law for create/edit. A nested one would trap the wheel over the rows and leave the card's edge
   saying nothing. */
.st-sepList{ display:flex; flex-direction:column; gap:8px; }
.st-sepRow{ flex:0 0 auto; display:flex; align-items:center; gap:10px;
  background:rgba(255,255,255,.07); border:1px solid rgba(255,255,255,.18); border-radius:11px; padding:9px 13px; }
.st-sepInfo{ flex:1; min-width:0; }
.st-sepName{ font:600 var(--t-caption)/1.25 Inter,sans-serif; color:#fff;
  white-space:nowrap; overflow:hidden; text-overflow:ellipsis; }
.st-sepUrl{ font-family:var(--mono); font-size:var(--t-mono); letter-spacing:var(--t-mono-ls);
  color:rgba(255,255,255,.55); white-space:nowrap; overflow:hidden; text-overflow:ellipsis; margin-top:3px; }
.st-sepCopy{ flex:none; border:1px solid rgba(255,255,255,.18); background:transparent; color:#fff;
  border-radius:9px; padding:6px 13px; cursor:pointer; font:600 var(--t-caption) var(--f); }
.st-sepCopy:hover{ background:rgba(255,255,255,.07); }
/* green = the receipt, never a green fill (same rule as "saved" on the edit row) */
.st-sepCopy.done{ border-color:rgba(48,209,88,.45); color:#30D158; }

/* ---- panel: name prompt, preview skips, picker tabs, active track row (feedback round 1) ---- */
.st-namePrompt{ font:600 11.5px/1 Inter,sans-serif; color:var(--blue-text); margin:0 0 6px 2px; }
.st-playRow{ display:flex; align-items:center; justify-content:center; gap:18px; }
.st-pmSkip{ flex:none; width:38px; height:38px; border-radius:50%; border:0; cursor:pointer;
  background:#2C2C2E; display:flex; align-items:center; justify-content:center; }
.st-pmSkip:hover{ background:#3A3A3C; }
.st-tkRow.on{ background:#2C2C2E; border-radius:8px; border-bottom-color:transparent; }
.st-tkRow.on .st-tkName{ color:#fff; }
.st-tkRow.on .st-tkNum{ color:var(--blue-text); }
.st-tkRow:not(.pending){ cursor:pointer; }
.st-tkPickTabs{ display:flex; gap:6px; margin-bottom:10px; }
.st-tkTab{ flex:1; background:none; border:1px solid rgba(255,255,255,.14); border-radius:9px;
  padding:7px 6px; cursor:pointer; font:600 12px Inter,sans-serif; color:rgba(255,255,255,.6); }
.st-tkTab.on{ border-color:var(--blue); color:#fff; background:rgba(59,116,240,.12); }
.st-tkPickHint{ margin-top:7px; text-align:center; font-size:11px; color:rgba(255,255,255,.32); }
/* disabled expiry option: visible, so the reason lands on the option itself */
.st-menu .mi.off{ opacity:.4; pointer-events:none; }
/* a collection's row: a second ring behind the play button says "there's more than one in here" */
.sv-row.stack .sv-rplay{ position:relative; }
.sv-row.stack .sv-rplay::before{ content:''; position:absolute; inset:-4px -4px -4px 4px; border-radius:50%;
  border:1.5px solid rgba(255,255,255,.22); z-index:-1; }
.sv-row.stack .sv-rplay::after{ content:''; position:absolute; inset:-7px -7px -7px 7px; border-radius:50%;
  border:1px solid rgba(255,255,255,.12); z-index:-1; }
/* drag a single track's row onto a collection row to add it */
.sv-row.dropTarget{ background:rgba(59,116,240,.14); border-radius:8px; box-shadow:inset 0 0 0 1.5px var(--blue); }
/* the same receipt on the panel's queue row: the picker's tick says "it is in", and this is where
   it lands. Scrolled into view by revealTrack, because a 3.8-row window appends out of sight. */
.sv-row.justAdded, #viewPanel .st-tkList .st-tkRow.justAdded{ animation:svAdded 1.1s ease; }
@keyframes svAdded{ 0%{ background:rgba(59,116,240,.30); } 100%{ background:transparent; } }

/* ---- round 2: stack row, filter, sub-row play, rename, panel auto-play ---- */
/* a collection has no play button — the chevron occupies that slot and IS the marker */
.sv-row.stack .sv-rplay{ display:none; }
/* NO order:-1. It pulled the chevron to the visual FRONT of the row, ahead of the drag handle, so a
   collection read chevron-first / dots-inward while a solo read dots-first. Moving the chevron in
   the markup could never fix that on its own — flex `order` beats DOM order, and a DOM-order
   assertion passes while the screen shows the opposite. Source order now governs: drag handle
   outermost on both row kinds, chevron in the slot the solo's play button occupies. The round grey
   button treatment stays; only the reordering goes. */
.sv-row.stack .sv-chev{ width:28px; height:28px; border-radius:50%; background:#2C2C2E;
  align-items:center; justify-content:center; margin-right:2px; }
.sv-row.stack .sv-chev:hover{ background:#3A3A3C; }
.sv-row.stack .sv-rplay::before, .sv-row.stack .sv-rplay::after{ content:none; }   /* stacked rings retired */
/* sub-rows gain their own small play button */
.sv-subPlay{ flex:none; width:22px; height:22px; border-radius:50%; border:0; cursor:pointer;
  background:var(--blue); display:flex; align-items:center; justify-content:center; padding:0; }
.sv-subPlay:hover{ filter:brightness(1.1); }
.sv-subRow.playing .sv-subName{ color:#fff; font-weight:600; }
/* all · collections filter */
.sv-filter{ display:flex; align-items:center; gap:5px; margin-right:9px; }
.sv-fbtn{ background:none; border:0; padding:2px 1px; cursor:pointer; font:500 11.5px Inter,sans-serif;
  color:rgba(255,255,255,.38); }
.sv-fbtn.on{ color:#fff; }
.sv-fdot{ color:rgba(255,255,255,.22); font-size:11px; }
/* inline rename */
.st-tkEdit{ flex:none; background:none; border:0; padding:2px; cursor:pointer; display:flex; border-radius:6px; opacity:.6; }
.st-tkEdit:hover{ opacity:1; background:rgba(255,255,255,.07); }
.st-tkRenameIn{ flex:1; min-width:0; background:#2C2C2E; border:1px solid var(--blue); border-radius:7px;
  padding:3px 7px; font:600 13px Inter,sans-serif; color:#fff; outline:none; }
/* the auditioning row shows its spec where the duration was */
.st-tkRow.spec .st-tkDur{ color:var(--blue-text); }
/* panel auto-play, positioned like the studio player's (play stays centred) */
.st-playRow{ position:relative; min-height:80px; }   /* keeps the play button's footprint while
   prev/next/auto are hidden mid-batch — without it the transport strip collapsed around a hidden
   button and the panel jumped between tracks */
/* (moved to sendbox.css — ONE shared control, see the SHARED CONTROLS block there) */
/* (moved to sendbox.css — ONE shared control, see the SHARED CONTROLS block there) */
/* (moved to sendbox.css — ONE shared control, see the SHARED CONTROLS block there) */
/* TOGGLE COLOUR RULE: green = playback preference (auto-play), blue = link setting (downloads) */
/* the two auto-play switches used to pin the STATUS green here, at a specificity that outranked
   the shared component (.sv-toggle.green is 3 selectors; #svAuto is an id). Both are auto-play, so
   both take --green-on from the component in sendbox.css and these overrides are gone. #dlToggle is
   NOT auto-play — it is the downloads switch — and keeps the status green from the .sv-toggle
   base above, deliberately: Sid's ruling is about the auto-play control. */

/* ---- round 3 ---- */
/* titles show in FULL and wrap to two lines; they are the play target (tap-to-expand retired) */
.sv-rname, .sv-subName, .st-tkName{ white-space:normal; overflow:visible; text-overflow:clip;
  overflow-wrap:anywhere; display:-webkit-box; -webkit-line-clamp:2; -webkit-box-orient:vertical;
  overflow:hidden; }
.sv-rname, .sv-subName, .st-tkName{ cursor:pointer; }
/* failed / cancelled upload rows: keep their place, say why, offer a retry */
.st-tkRow.failed{ align-items:flex-start; }
.st-tkRow.failed .st-tkName{ color:rgba(255,255,255,.5); }
.st-tkWhy{ flex-basis:100%; order:9; font-size:11px; line-height:1.4; color:#FF9F0A; padding:2px 0 0 24px; }
.st-tkRetry{ order:8; flex:none; background:none; border:1px solid rgba(255,255,255,.2); border-radius:999px;
  padding:2px 9px; cursor:pointer; font:600 11px Inter,sans-serif; color:#fff; }
.st-tkRetry:hover{ border-color:var(--blue); }
.st-tkAbandon{ background:none; border:0; padding:0; cursor:pointer; font:600 11.5px Inter,sans-serif; color:#FF453A; }
.st-tkAbandon:hover{ text-decoration:underline; }
/* the "switched to custom order" notice */
.sv-toast{ position:fixed; left:50%; bottom:26px; transform:translateX(-50%) translateY(8px); z-index:800;
  background:#2C2C2E; border:1px solid rgba(255,255,255,.14); border-radius:999px; padding:8px 16px;
  font:600 12px Inter,sans-serif; color:#fff; opacity:0; pointer-events:none; transition:opacity .18s, transform .18s;
  box-shadow:0 10px 30px rgba(0,0,0,.5); }
.sv-toast.on{ opacity:1; transform:translateX(-50%) translateY(0); }
/* empty collections filter state + head alignment */
.sv-tracksHead{ align-items:baseline; }
.st-sectlabel, .sv-fbtn{ font-size:11.5px; letter-spacing:.06em; }
/* short spec form inside rows: "44.1 / 24" */
.sv-loadedFmt .short{ font-family:'Roboto Mono',monospace; }

/* ---- what a batch becomes (one collection vs separate links) ----
   The inline pills that used to live here are GONE: they were missable, and a first-time user
   sailed straight past them into an outcome they hadn't chosen. The question is now the
   #qmodeModal dialog, styled by the shared .acmodal rules above. */
/* the row whose file is going up right now — the well names it, this makes it findable in the list */
.st-tkRow.uploading{ background:rgba(59,116,240,.10); box-shadow:inset 2px 0 0 var(--blue); }
.st-tkRow.uploading .st-tkName{ color:#fff; }
.st-tkRow.uploading .st-tkDur{ color:var(--blue-text); font-weight:600; }

/* temporary drop diagnostic: how many files the browser actually handed over */
/* furniture in the queue header, not a headline of its own: smallest quiet mono, matched to the
   track count it sits beside, and it takes the middle of the row so neither end control moves. */
.st-tkDiag{ flex:0 1 auto; min-width:0; overflow:hidden; text-overflow:ellipsis; white-space:nowrap;
  font:400 10px/1 'Roboto Mono',monospace; letter-spacing:.02em; color:rgba(255,255,255,.3);
  text-align:center; padding:0 8px; }

/* an unanswered batch's collection: quiet, one line, one tap out of it. Deliberately NOT a warning
   colour — nothing is broken, it is just an outcome the producer never actually picked. */
.sv-rflag{ font:500 11px Inter,sans-serif; color:var(--ink-2); margin-top:3px; line-height:1.35; }
.sv-rsplit{ background:none; border:0; padding:0; cursor:pointer; font:600 11px Inter,sans-serif;
  color:var(--blue-text); text-decoration:underline; text-underline-offset:2px; }
.sv-rsplit:hover{ color:#fff; }
.sv-rsplit[disabled]{ color:var(--ink-2); text-decoration:none; cursor:default; }

/* ============================================================================
   PANEL — collection visual pass (items 1, 2, 5, 8). Appended at the END so these
   rules win outright; this section carries its own phone block at the bottom.
   ============================================================================ */

/* item 2 — the auditioning row's tech line, matched to the recipient player. Quiet mono, NOT
   blue: blue is the interactive tint here too, the row number already carries it to mark
   "playing", and two blues on one row is noise. (Was blue since round 2; unified now that the
   same treatment has to read identically on both surfaces.) */
.st-tkRow.spec .st-tkDur{ color:rgba(255,255,255,.6); letter-spacing:-.2px; }

/* item 8 — the standing drop meaning on a finished single's well. Centred, quiet, and clearly a
   label rather than a control: it sits on the well's scrim, not on the waveform's business. */
/* a CAPTION under the well, same register as the panel's other captions (.st-pmFmt / .st-tkNote):
   centred, quiet, no pill and no background. It was a chip ON the waveform, competing with the two
   time readouts for the tightest space on the card. */
.st-wellHint{ text-align:center; margin-top:9px; font:400 11.5px/1.3 Inter,sans-serif;
  color:rgba(255,255,255,.4); }

/* item 5 — the panel's tracklist is the same WINDOW as the recipient's: recessed edge, its own
   inset ground, bottom fade, a half-cut row at overflow. item 1's "no stray scrollbar" is the
   same rule: the fade is the affordance, so the scrollbar is removed rather than styled. */
.st-tkList{ border-radius:10px; padding:1px 10px;
  background:rgba(0,0,0,.20);
  box-shadow:inset 0 1px 2px rgba(0,0,0,.55), inset 0 0 0 1px rgba(255,255,255,.055); }
/* THE WINDOW IS MEASURED, NOT DECLARED (Sid, 2026-08-02 — the same rule the player's tracklist
   already follows). `calc(37px * 3.8)` was a magic number that only ever matched one kind of row:
   these rows are min-height 44 and render at ~47, so 140.6px came to exactly 3.0 of them — a whole
   row cut cleanly at the edge, which makes a 5-track queue read as a 3-track one. studio.js sizes
   it from the row it actually rendered, to a half-row peek, and sets .scroll from real overflow
   rather than from a track count (see fitPanelWindow). */
.st-tkList.scroll{ overflow-y:auto; scrollbar-width:none; }
/* and the fade is a claim about what is below, so it goes when there is nothing below — .more is
   set from the scroll position, exactly as on the player's list */
.st-tkList.scroll.more{
  -webkit-mask-image:linear-gradient(180deg,#000 0,#000 calc(100% - 8px),transparent 100%);
  mask-image:linear-gradient(180deg,#000 0,#000 calc(100% - 8px),transparent 100%); }
.st-tkList.scroll::-webkit-scrollbar{ width:0; height:0; }
.st-tkList .st-tkRow{ border-bottom:1px solid rgba(255,255,255,.055); }
.st-tkList .st-tkRow:last-child{ border-bottom:none; }

/* ---- item 7: the same three-step scale on the panel ------------------------------------------- */
.st-player + .st-tkDiag{ margin-top:14px; }
#viewPanel .st-tracks{ margin:16px 0 18px; }        /* transport/spec → the window → the fields */
#viewPanel .st-tkHead{ margin-bottom:8px; }
#viewPanel .st-pmFmt{ margin-top:10px; }
#viewPanel .st-tkNote{ margin-top:10px; }
@media (max-width:480px){
  #viewPanel .st-tracks{ margin:12px 0 14px; }
}

/* ---- one type treatment across the tracks-header row ----------------------------------------
   "your tracks" and "all · collections" were ALREADY the same size with the same letter-spacing
   (11.5px / 0.69px, measured). The row read uneven because the label is Roboto Mono and the filter
   is Inter, and mono's advance width is simply wider — so tightening letter-spacing would have
   chased the symptom and never matched. Same family and weight as the filter; the label keeps its
   quieter colour, because it labels the row rather than acting on it. */
.sv-tracksHead .st-sectlabel{ font-family:Inter,-apple-system,sans-serif; font-weight:500; }

/* ---- auto-play joins the transport CLUSTER (all three surfaces identical) ---------------------
   It was position:absolute right:0, which parked it hard against the card edge, wide of the
   buttons and optically high — a stray control rather than part of the transport. Now it is a
   normal flex member sitting one row-gap from the next arrow, align-self:center so the toggle and
   its label share the arrows' centreline.
   PLAY STAYS DEAD-CENTRE: taking it out of absolute positioning would otherwise push the hero left
   by its own width, so the row carries a mirrored 38px spacer when auto is present. 38px is the
   control's measured natural width (36.2px label, the widest child) rounded up — the spacer and the
   control must be equal or the centring is a near-miss. — the "keep these numbers in step with the
   .vp-auto block in player.css" instruction that used to end this note is gone with the copies:
   the control is ONE component in sendbox.css, which the player DOES load. */
.sv-transport, #viewPanel .st-playRow{ align-items:center; }
/* (moved to sendbox.css — ONE shared control, see the SHARED CONTROLS block there) */

/* (moved to sendbox.css — ONE shared control, see the SHARED CONTROLS block there) */

/* (moved to sendbox.css — ONE shared control, see the SHARED CONTROLS block there) */

/* (moved to sendbox.css — ONE shared control, see the SHARED CONTROLS block there) */

/* (moved to sendbox.css — ONE shared control, see the SHARED CONTROLS block there) */
/* the spacer exists ONLY while auto does, or a hidden control would shift the cluster off-centre */
/* (moved to sendbox.css — ONE shared control, see the SHARED CONTROLS block there) */


/* ---- ONE WINDOW COMPONENT: the studio's expanded sub-list gets the recipient tracklist's
   treatment — recessed inset edge, its own darker ground, a bottom fade over the cut row. An
   expansion used to be flat rows on the card, so a long collection just ran off with nothing
   saying so; the two surfaces now read as the same object.
   SCROLLBAR CLEARANCE: it grazed the track timings. Thin overlay style with the gutter reserved in
   padding, so the mono durations never sit under it. ---- */
.sv-sub{ position:relative; margin:2px 0 8px 34px; padding:2px 0 4px; border-bottom:none;
  border-radius:10px; background:rgba(0,0,0,.20);
  box-shadow:inset 0 1px 2px rgba(0,0,0,.55), inset 0 0 0 1px rgba(255,255,255,.055); }
/* (the old padding-left:10/padding-right:16 "scrollbar lane" rule lived here — dead since the
   panel pass set the shorthand 10px 12px on the same selector later in the file, and the lane
   itself is gone now the scrollbar is the OS overlay, which draws over content by design) */
.sv-sub.scroll{ overflow-y:auto; max-height:calc(31px * 3.8);
  -webkit-mask-image:linear-gradient(180deg,#000 0,#000 calc(100% - 8px),transparent 100%);
  mask-image:linear-gradient(180deg,#000 0,#000 calc(100% - 8px),transparent 100%); }
/* same law as the studio list: the reference has NO scrollbar styling, so neither does the
   expansion window — native overlay, hidden at rest, visible only while scrolling */
/* the release stripe still belongs to the envelope, and must clear the new rounded ground */
.sv-row.dl + .sv-sub::before{ left:-11px; bottom:4px; }

/* ---- an OPEN collection is ONE block ----------------------------------------------------------
   The row divider normally sits under every top-level row. On an expanded collection that line fell
   between the header and its OWN sub-rows, slicing the block it belongs to in half and making the
   tracks look like they belonged to whatever came next. When open, the header's line goes and the
   separator moves BELOW the window — marking where the next TOP-LEVEL row begins, which is what a
   separator is for. Collapsed rows are untouched.
   It is drawn on the sub's ::after (::before is the blue release stripe) and pulled back out of
   the indent so it spans the full row width like every other divider. The pull-back MIRRORS the
   panel's own margins via --sub-indent/--sub-outdent: it was a bare -34px, the indent of the
   generation that wrote it, so when the panel moved to 60px the line started 26px short of the
   left edge and nobody's harness looked (Sid's report, 2026-07-30 — the reference runs it to the
   row edge on both sides, measured). */
.sv-row.open{ border-bottom-color:transparent; }
/* THE SEPARATOR IS DRAWN BY THE NEXT ROW, not by the panel, and that is the fix for the horizontal
   scrollbar Sid photographed across this panel's bottom edge. It used to be an ::after on .sv-sub
   pulled out of the indent to span the full row width — `right:-7px` at desktop, `-6px` at 390,
   which is 7px (6px) of SCROLLABLE OVERFLOW inside a box that becomes overflow-y:auto the moment a
   collection has more than four tracks. overflow-x then computes to auto beside it, and a machine
   set to "always show scroll bars" draws a full-width classic bar for those 7px. Measured before
   the fix: client 298, scrollWidth 305, and 12px of vertical overflow from `bottom:-12px` on top.
   A pseudo-element of the SCROLLER can only ever be inside the scroller, so no offset fixes this —
   the line has to belong to something else. The next top-level row is the honest owner anyway: the
   separator marks where that row begins, it spans the full width for free, and where there is no
   next row there is correctly no line (which is the same rule .sv-row:last-child already follows).
   The panel's 12px margin-bottom is what puts the line exactly where the ::after drew it. */
.sv-sub + .sv-row{ border-top:1px solid var(--row-line); }

/* item 4: the tracks header is now filter (left) + sort (right) — the label is gone, so the row no
   longer needs the label's baseline alignment to hold it together. */
.sv-tracksHead .sv-filter{ margin-right:auto; }
.sv-tracksHead .st-sectlabel{ display:none; }   /* belt: any cached markup still carrying it stays quiet */

/* ---- TYPE SCALE on the panel, so the family stays matched with the player cards --------------
   Same four voices, same tokens (declared here too: the studio never loads player.css).
   The SPACING tokens are declared here for the same reason — they were defined only in player.css,
   so every `var(--sp-*)` written in this file silently resolved to nothing and the rule did not
   apply at all. That is how the panel and studio-preview title->spec air measured 0px while the
   player card measured 20px: not a tuning difference, a dead variable. */
:root{
  /* THE STUDIO'S OWN VOICES. The four shared type voices, their tracking, the weights and the five
     spacing steps moved to tokens.css on 2026-08-01 so the player inherits ONE scale instead of
     agreeing with a copy of it — see the note there. What is left here is what only the studio has.

     THE ROW BLOCK (Sid, 2026-07-31), measured off the reference. This OVERTURNS the earlier snap of
     row titles to --t-body 14.5: that snap was a judgement about keeping the scale to four voices,
     and the reference simply does not render it that way. Measured: title 15.5/600, the "N days
     left" urgency run 12/600, sub-row title 14.5/400. The meta's base ("3 tracks ·") measured 13 on
     BOTH sides and is unchanged — the "meta smaller" Sid saw is the urgency run, which was 12.5. */
  --t-rowtitle:15.5px;         /* collection + solo row titles */
  --t-rowdays:12px;            /* the "N days left" run inside row meta */
  /* (--t-subrow moved to tokens.css 2026-08-01: the players' tracklists take the same voice now) */

  /* ---- COLLECTION CREATE/EDIT PANEL (Acetate_Collection.html, 2026-07-31) ----
     Every value below was read headless off that reference, not taken from the brief's table —
     the table is orientation, the render is the authority (it is what caught "glyph 25" being a
     font-size rather than a painted size, twice now). The panel's SURFACES and INKS moved to
     tokens.css with the rest of the ramp; these are its type voices, which only it has. */
  --t-panelfield:15.5px;       /* name field VALUE */
  --t-panelmsg:14.5px;         /* message field / placeholder */
  --t-panellabel:13px;         /* "disappears in", "N tracks", row number + duration */
  --t-panelurl:14px;           /* the link row's url */
  --t-panelsave:15.5px;        /* save changes */
  --t-paneldel:14px;           /* delete link */
}
#viewPanel .st-pmFmt,
#viewPanel .st-tL, #viewPanel .st-tR,
#viewPanel .st-tkNum, #viewPanel .st-tkDur,
#viewPanel .st-tkCount, #viewPanel .st-tkDiag{ font-size:var(--t-mono); letter-spacing:var(--t-mono-ls); }
#viewPanel .st-tkName{ font-size:var(--t-caption); }
#viewPanel .st-tkNote, #viewPanel .st-tkWhy{ font-size:var(--t-caption); }
/* (moved to sendbox.css — ONE shared control, see the SHARED CONTROLS block there) */

/* the studio LIST's own mono readouts join the same treatment — one mono product-wide */
/* .sv-subNum/.sv-subDur left this list 2026-07-30: they stopped being mono at the family fix and
   the reference tracks them normal — the later sub-row rule owns them now. */
.sv-loadedFmt{ letter-spacing:var(--t-mono-ls); }
/* (the ≤480 step-down for the shared scale moved to tokens.css with the scale itself) */

/* the rest of the panel joins the scale — nothing on this surface sits outside it */
#viewPanel .st-flabel, #viewPanel #expLabelTxt,
#viewPanel #copyBtn, #viewPanel #shareBtn, #viewPanel #saveBtn{ font-size:var(--t-body); }
#viewPanel .st-tkAdd, #viewPanel .st-dlNote, #viewPanel #delBtn{ font-size:var(--t-caption); }
#viewPanel #urlText{ font-size:var(--t-mono); letter-spacing:var(--t-mono-ls); }

/* item 1: the SAME title -> spec relationship on the panel and the studio preview */
#viewPanel .st-pmFmt{ margin-top:var(--sp-tight); }
.sv-loaded .sv-loadedFmt{ margin-top:var(--sp-tight); }

/* item 2: the well's time chips are numeric readouts like every other — same family, same size
   step, same tracking. .sv-tL/.sv-tR were the drift: right family and size, but no --t-mono-ls, so
   they tracked wider than every other number in the product. All wells, all surfaces. */
.st-tL, .st-tR, .sv-tL, .sv-tR{
  font-family:var(--mono),'Roboto Mono',monospace;
  font-size:var(--t-mono); letter-spacing:var(--t-mono-ls); }
/* the sort control's label joins the scale too (it was a stray 11.5px) */
.sv-sortTxt{ font-size:var(--t-caption); }


/* the panel and the studio rows take the SAME moved scale — the design session only drew the
   player card, but the tokens are shared, so these follow rather than drift. */
#viewPanel .st-pmName{ font-weight:var(--t-w-bold); letter-spacing:var(--t-title-ls); }
#viewPanel .st-pmFmt{ font-size:var(--t-caption); letter-spacing:var(--t-tracking); }
/* well timecodes are Inter on every player well now (the design retires mono from the card face);
   the studio LIST's numbers and durations keep the mono family. */
#viewPanel .st-tL, #viewPanel .st-tR, .sv-tL, .sv-tR{
  font-family:Inter,-apple-system,sans-serif; font-weight:var(--t-w-med);
  letter-spacing:var(--t-tracking); color:var(--txt-lead); }


/* ============================================================================================
   STUDIO — approved reference, mapped onto the shared tokens. Read headless from the mock; no
   markup, class or rule imported. Families are ours (Inter): the export's Helvetica/Arial are
   fallbacks it had no webfont for, not values.

   SNAPPED to the nearest declared step rather than minting new ones (no widening, no fudging):
     auto-play    12   -> --t-mono   (12.5)
     days left    12   -> --t-mono   (12.5)
     row title    15.5 -> --t-body   (14.5)   same value Sid already ruled on for the player
     ...          15   -> --t-body   (14.5)
     avatar init  17   -> --t-body   (14.5)
   ============================================================================================ */

/* --- header --- */
.st-wordmark{ font-size:var(--t-title); font-weight:var(--t-w-med);
  letter-spacing:var(--t-logo-ls); color:var(--txt-bright); }
.st-avatar{ width:30px; height:30px; background-color:#fff; color:var(--ink-invert);
  font-size:var(--t-body); font-weight:var(--t-w-bold); font-style:normal; }
#viewStudio .sv-upload span, .sv-uploadMore span{ font-size:var(--t-body);
  font-weight:var(--t-w-semi); letter-spacing:var(--t-tracking); color:var(--txt-bright); }

/* --- preview player: same voices as the recipient card --- */
.sv-loadedName{ font-size:var(--t-title); font-weight:var(--t-w-bold);
  letter-spacing:var(--t-title-ls); color:var(--txt-bright); }
.sv-loadedFmt{ font-family:Inter,-apple-system,sans-serif; font-size:var(--t-caption);
  font-weight:var(--t-w-reg); letter-spacing:var(--t-tracking); color:var(--txt-muted); }

/* --- transport --- */
.sv-play{ width:84px; height:84px; background:var(--blue); }
.sv-tbtn{ width:44px; height:44px; background:#2A2B2E; }
/* (moved to sendbox.css — ONE shared control, see the SHARED CONTROLS block there) */
/* (moved to sendbox.css — ONE shared control, see the SHARED CONTROLS block there) */
/* (moved to sendbox.css — ONE shared control, see the SHARED CONTROLS block there) */
/* (moved to sendbox.css — ONE shared control, see the SHARED CONTROLS block there) */

/* --- filter row: smaller and quieter than it was --- */
.sv-fbtn{ font:var(--t-w-reg) var(--t-caption) Inter,sans-serif; letter-spacing:0;
  color:var(--txt-muted); }
.sv-fbtn.on, .sv-fbtn[aria-pressed="true"]{ color:var(--txt-bright); }
.sv-fdot{ font-size:var(--t-caption); color:#55585C; }

/* --- sort control: the labelled box retires, chevron only --- */
.sv-sortBtn{ width:26px; height:19px; padding:0; gap:0; justify-content:center; }
.sv-sortTxt{ display:none; }

/* --- rows --- */
/* line-height DECLARED, not left to `normal`. The reference declares 19.375/15.5 = 1.25 on the
   title and 15.6/13 = 1.2 on the meta; live inherited `normal`, which is a FAMILY-DEPENDENT value
   — the same trap as the tracking, one level up. With the title at its new 15.5 the row had grown
   to 66 against the reference's 63, because Inter's normal line box is taller than Helvetica's AND
   the 6px meta margin was stacking on top of that slack. Declaring both puts the row back on the
   reference's geometry at both widths. */
.sv-rname{ font-size:var(--t-rowtitle); font-weight:var(--t-w-semi); line-height:1.25;
  letter-spacing:var(--t-tracking); color:var(--txt-bright); }
.sv-rmeta{ font-size:var(--t-caption); font-weight:var(--t-w-reg); line-height:1.2;
  letter-spacing:var(--t-tracking); color:var(--txt-muted); }
/* row circles: 34 either way — surface-2 chevron on a collection, blue play on a track */
.sv-chev, .sv-row.stack .sv-chev{ width:34px; height:34px; border-radius:50%; background:#2A2B2E;
  align-items:center; justify-content:center; padding:0; }
.sv-rplay{ width:34px; height:34px; background:var(--blue); }

/* the kebab reads as the mock's glyph. The SVG stays in the DOM (its aria-label is the accessible
   name); only its painting changes, so no markup or behaviour moved. */
.sv-rkebab{ align-items:center; justify-content:center; }
.sv-rkebab svg{ display:none; }
.sv-rkebab::before{ content:'\00B7\00B7\00B7'; font-size:var(--t-body); font-weight:var(--t-w-reg);
  letter-spacing:1.2px; color:var(--txt-muted); line-height:1; }

/* days left keeps the URGENCY SCALE — the reference paints every row red, including "7 days left",
   which would throw away the signal. Only the RED VALUE is adopted from it. */
.sv-rmeta .x{ font-size:var(--t-rowdays); font-weight:var(--t-w-semi); letter-spacing:var(--t-tracking); }
/* no .soon/.urgent rules here on purpose: studio.js sets the urgency colour INLINE (timeLeft()),
   so class-based rules would be dead code masquerading as coverage. The scale lives there. */


/* ============================================================================================
   Two fixes against the studio reference.

   1. AUTO-PLAY ON THE TRANSPORT CENTRELINE. The wrapper is a flex column holding the pill and its
      label, and the ROW centres that whole column — so the pill rode (label + gap)/2 above the
      centreline, measured 9px high against the reference's exact 0. Only the pill participates in
      the row's centring now; the label hangs beneath it at --sp-tight, which is the 6px the
      reference measures between pill bottom and label top.
   2. THE SORT BOX RETIRES. The reference is a bare glyph: no border, no background, no radius.
      The 26x19 hit area is untouched — only the painting goes, so the target stays thumb-sized.
   ============================================================================================ */
/* (moved to sendbox.css — ONE shared control, see the SHARED CONTROLS block there) */

/* (moved to sendbox.css — ONE shared control, see the SHARED CONTROLS block there) */

.sv-sortBtn{ border:0; background:none; border-radius:0; padding:0; }
/* open state can no longer be a box, so the glyph itself brightens */
.sv-sortBtn.open{ border-color:transparent; background:none; }
.sv-sortBtn svg{ opacity:.72; transition:opacity .15s ease; }
.sv-sortBtn:hover svg, .sv-sortBtn.open svg{ opacity:1; }


/* sort glyph 25% up (13 -> 16.25, chevron 9 -> 11.25). Sized in CSS rather than the SVG attributes
   so the two stay locked to one ratio. The 26x19 hit area is deliberately unchanged: with the box
   retired there is no border or fill to overflow, so the glyph can exceed it harmlessly while the
   tap target stays the size it was. */
.sv-sortBtn > svg{ flex:none; width:16.25px; height:16.25px; }
.sv-sortBtn .sv-sortChev{ flex:none; width:11.25px; height:11.25px; }
/* the two glyphs together are wider than the 26px hit area, so without flex:none the row SQUASHED
   them — 15.36 wide against 16.25 tall, i.e. square icons rendering non-square. */


/* ============================================================================================
   EXPANDED-COLLECTION REFERENCE PASS. Values read headless from the mock's expanded state
   (chevron clicked), not from prose. Nothing imported.

   Component dimensions, NOT layout gaps — same class as the hero's 88px and the pill's padding,
   so they are deliberately off the 6/12/18/24/30 spacing scale and out of the spacing harness:
   the 9px alignment inset (Sid's call: component inset, do not snap), the panel's 61/7 indents,
   the sub-row's 47px height and 10/12 padding, and the 10px sub-row play glyph.
   The type all lands ON steps: number + duration --t-caption, title --t-body, glyphs --t-caption.
   ============================================================================================ */

/* card padding matches the reference AND the solo player card, which was already 24/22 —
   the studio was the transposed one (22 vertical / 24 horizontal). */
.st-card{ padding:24px 22px; }

/* the 9px inward alignment: filter row from the left, sort + avatar from the right, so the sort
   control's right edge and the avatar's right edge sit on one line. */
/* each of these sits in a container with its own 1px (and, for the avatar, 8px) offset from the
   card's content edge, so the margin is the DELTA that lands the measured 9px, not 9 itself.
   Verified by measuring the served page, not by assuming the containers align. */
.sv-filter{ margin-left:8px; }
.sv-headR{ margin-right:8px; }
/* the avatar is position:absolute and an #id rule zeroes its margins, so margin-right was never
   the lever — computed 0px however large it was set. `right` is. Its parent sits 1px inside the
   card content edge, so right:8px lands the avatar's right edge on the sort control's (9px in). */
#avatar.st-avatar{ right:8px; }

/* the inset sub-row panel. The indents live in custom props because the OPEN-collection divider
   below must mirror them exactly: its old left:-34px was the previous generation's indent kept as
   a constant, so when the panel moved to 60px the line silently started 26px short of the row
   edge (Sid's report). A constant rots; the prop cannot.
   The EDGE is the reference's, measured headless: a faint 1px ring in #2C2E31 — the same ink as
   the sub-row separators — and NO inset shadow. box-shadow:none is load-bearing: the older
   generation's rule still declares the recessed inset pair, and at 0.055 alpha it read as no
   border at all rather than as the reference's edge. */
/* VERTICAL AIR, measured off the reference 2026-07-30: it sits EVEN, 12 above and 12 below.
   Above is not a margin there — the panel butts straight onto the collection row's box and the
   row's own 12px bottom padding IS the air, so margin-top goes to 0 (live's extra 2px made it 14
   above against 5 below: tight AND lopsided). Below, the next row begins 12px down and the divider
   is drawn exactly there. 12 is --sp-close's value, held FLAT at both widths like the row slack it
   pairs with — the panel's HORIZONTAL indent still follows the phone scale via the props. */
.sv-sub{ --sub-indent:60px; --sub-outdent:6px;
  background:#202124; border-radius:12px; padding:0;
  margin-top:0; margin-bottom:12px;
  margin-left:var(--sub-indent); margin-right:var(--sub-outdent);
  border:1px solid #2C2E31; box-shadow:none; }
/* .sv-sub .sv-subRow, because that pair already sets padding-left/right and outranks a bare class.
   Re-measured against the reference 2026-07-30 (airy ruling): single-line 47 with 10px slack each
   side, a two-line row keeps the SAME slack and grows — min-height + padding is exactly that pair,
   so both cases read as one family rather than content-hugged. The internal gap is the reference's
   12px, and it lands ON a declared step: --sp-close is 12 at desktop and 9 at 390, which keeps the
   phone's ruled tighter indent scale without a second rule. */
.sv-sub .sv-subRow{ min-height:47px; height:auto; box-sizing:border-box; padding:10px 12px; background:none; gap:var(--sp-close); }
/* separators BETWEEN rows only — the first row has none, exactly as the reference measures */
.sv-subRow + .sv-subRow{ border-top:1px solid #2C2E31; }

/* sub-row parts */
.sv-subPlay{ width:26px; height:26px; background:var(--blue); }
.sv-subPlay svg{ width:17.4px; height:17.4px; }
/* FAMILY, not just size. These inherited the studio's mono treatment and I changed only their
   size/weight/tracking/colour, so they stayed Roboto Mono while the table I checked said "13px,
   #8E9196 — matches". The reference has them sans. Never assert a subset of what you changed. */
/* tracking: NORMAL, not the shared -0.005em — the reference measures its sub-row numerals
   (number + duration) untracked while every text role around them carries --t-tracking. Verified
   headless 2026-07-30 on the expanded state; adopting it is what the "tighter and slicker" diff
   actually came down to for numerals. */
.sv-subNum, .sv-subDur{ font-family:Inter,-apple-system,sans-serif;
  font-size:var(--t-caption); font-weight:var(--t-w-reg);
  letter-spacing:normal; color:var(--txt-muted); }
/* SID'S RULING, overrides the reference: a track's title is shown IN FULL, never ellipsised. It
   wraps to a second line and the row grows to fit. Truncating a title hides which take you are
   about to play, which matters more than a uniform row height. */
.sv-subName{ font-family:Inter,-apple-system,sans-serif;
  font-size:var(--t-body); font-weight:var(--t-w-reg);
  letter-spacing:var(--t-tracking); color:var(--txt-bright);
  white-space:normal; min-width:0; overflow-wrap:anywhere;
  /* FINAL: two lines maximum, ellipsis only beyond two — the clamp mechanism, used deliberately
     this time rather than fought. overflow MUST stay hidden: with the clamp active and overflow
     visible, WebKit paints the clamped text and the overflow together, which is the double-render
     that showed a clipped and a full title at once. */
  display:-webkit-box; -webkit-line-clamp:2; -webkit-box-orient:vertical; overflow:hidden;
  text-overflow:ellipsis; }

/* one declared step down (--t-body -> --t-caption), Sid's overrule of the reference so long names
   fit in two lines more often. */
.sv-sub .sv-subName{ font-size:var(--t-subrow); }

/* PLAYING changes the circle and the COLOUR only. It must not change size or weight: reflowing the
   type on play made a two-line title jump to three, or back to one. */
.sv-subRow.playing .sv-subName{ font-weight:var(--t-w-reg); font-size:var(--t-subrow); color:#fff; }

/* the hero title above the transport wraps to two lines on its own — no tap. The .expanded escape
   hatch set overflow:visible, which with the clamp is the same double-render; it is neutralised. */
.sv-loadedName, .sv-loadedName.expanded{
  white-space:normal; overflow-wrap:anywhere;
  display:-webkit-box; -webkit-line-clamp:2; -webkit-box-orient:vertical;
  overflow:hidden; text-overflow:ellipsis; }

/* row glyphs are 13 in this reference; the expanded chevron circle keeps surface-2 */
/* GLYPH : CIRCLE (Sid, 2026-07-30). The reference's PAINTED triangle fills 0.279 of its circle at
   both row levels; live's filled 0.16, which is what read small.
   0.279 and not the 0.338 first shipped: the reference's glyph is the TEXT "▶", and a Range rect
   returns its ADVANCE box, which is wider than the ink. Sizing from the advance overshot by ~20%
   and Sid caught it as too big. Canvas actualBoundingBoxLeft/Right gives the painted extent:
   7.28px in a 26 circle, 9.46 in a 34. This is the same box-vs-ink error corrected elsewhere in
   this pass, made in my own measurement — hence the value here is the INK ratio, stated as such.
   Live's glyph is an svg path inset in its viewBox (ink is 10/24 of the box), so the box is
   0.279/0.4167 = 0.670 of the circle: 26 -> 17.4, 34 -> 22.8.
   NB the two triangles are not the same shape — Arial's ▶ is 0.87 wide-to-tall, this path is 0.77 —
   so matching the WIDTH (what reads as size on a right-pointing glyph) leaves live's marginally
   taller. Matching both is not possible without redrawing the path, which is not on the table.
   The chevron leaves this rule: it is not a play glyph and only ever shared the line by
   coincidence of size. */
.sv-chev svg{ width:13px; height:13px; }
.sv-rplay svg{ width:22.8px; height:22.8px; }
.sv-row.open .sv-chev{ background:#2A2B2E; }

/* upload row gets a filled surface rather than a bare outline */
.sv-upload, .sv-uploadMore{ background:#1E1F22; border-radius:12px; }

/* DRAG HANDLES STAY — reorder behaviour untouched, only the painting changes: the reference's
   2px round dots, two columns by three rows on a 5px pitch. The 6px padding keeps a 19x24 hit
   area around a 7x12 dot field, so dragging is no harder than before.
   ONE TREATMENT, ONE RULE (Sid, 2026-07-30). The reference renders the identical handle at both
   levels; live had TWO implementations of it — this level drew the dots as a repeating
   radial-gradient with the svg hidden, the sub-rows drew an svg — and they had drifted: the
   gradient rendered at full strength while the sub-rows rendered at 0.5, which is the difference
   that showed. Both now use the same svg and the same rule below; the gradient is gone. */


/* PHONE: the desktop reference's 61px indent does not translate — at 390 it eats the title column,
   so a name that fits two lines on desktop needs three. Indent and inner padding come down to the
   phone scale's own steps (wide 23 / tight 5, and close 9 horizontally) which hands the title
   column ~45px back. Order is untouched: dots, circle, number, title, duration. The two-line cap
   and the 13px title hold at both widths. Proportions are my judgement — the reference is
   desktop-only. */
@media (max-width:480px){
  /* retune the PROPS, not the margins: the margins and the open-collection divider both read the
     props, so the phone indent and the divider span can never disagree again */
  .sv-sub{ --sub-indent:var(--sp-wide); --sub-outdent:var(--sp-tight); }
  .sv-sub .sv-subRow{ padding:10px var(--sp-close); }
}


/* the batch caption ("received 2 files · separate links") shares a flex line with "+ add tracks"
   and was being crushed into an ellipsis and wrapping awkwardly beside it. It is furniture, not a
   headline, so it takes its own full-width line beneath the action rather than competing for the
   same one. Gap from the spacing scale. */
.st-tkHead{ flex-wrap:wrap; row-gap:var(--sp-close); }
.st-tkHead .st-tkDiag{ flex:1 0 100%; order:2; white-space:normal; overflow:visible;
  text-overflow:clip; text-align:left; }


/* ==============================================================================================
   COLLECTION CREATE/EDIT PANEL — to Acetate_Collection.html (Sid, 2026-07-31)
   Scoped to #viewPanel wherever a rule would otherwise reach the studio list, which has its own
   reference and its own settled values. Tokens only; the ruled tracking compensation applies on
   top of every size here, and the numerals stay exempt exactly as they are in the list.
   ============================================================================================== */

/* the WELL is a transparent window with its own edge, not a filled box */
/* height from the shared component (sendbox.css), tiers included. This rule is id-scoped, so its
   hardcoded 120 outranked the shared .st-well and the studio never compacted on a short window
   while the player did — which is exactly the "player well reads smaller" difference. */
/* NO height here any more. It restated var(--well-h) — harmless while that was the only answer, and
   an id-scoped block on the shared component the moment there were two: .st-card.has-list's compact
   state is a class rule and this would have outranked it, so the panel would have kept the full
   well while the player compacted. The shared component owns the height; this owns the chrome. */
#viewPanel .st-well{ border-radius:14px; clip-path:inset(0 round 14px);
  border:1px solid var(--line-well); }
#viewPanel .st-playhead{ width:2.5px; }
/* time chips: the reference's ground is a hair lighter and squarer than live's */
#viewPanel .st-tL, #viewPanel .st-tR{ background:rgba(8,8,9,.72); border-radius:5px; padding:3px 8px;
  font-size:var(--t-mono); color:var(--ink-quiet); }

/* transport: the hero and its skips both grow, and the glyphs are sized by PAINTED INK, not by the
   font-size the brief quotes — ref ink 18.19 in an 84 circle (0.217) and 12.64 in a 44 (0.287).
   Live's glyphs are svg paths inset in their viewBox: the play path spans 10/24 of its box, the
   skip path 12/24, so the boxes are ink/0.4167 and ink/0.5. */
#viewPanel .st-play{ width:84px; height:84px; }
#viewPanel .st-play svg{ width:43.7px; height:43.7px; }
#viewPanel .st-pmSkip{ width:44px; height:44px; background:#2A2B2E; }
#viewPanel .st-pmSkip svg{ width:25.3px; height:25.3px; }

/* queue header */
#viewPanel .st-tkAdd{ font-size:var(--t-body); font-weight:var(--t-w-semi); letter-spacing:var(--t-tracking); }
#viewPanel .st-tkCount{ font-family:Inter,-apple-system,sans-serif; font-size:var(--t-panellabel);
  font-weight:var(--t-w-reg); letter-spacing:var(--t-tracking); color:var(--txt-muted); }

/* the track panel: a filled inset with its own edge, like the studio's expansion */
#viewPanel .st-tkList{ background:var(--surface-inset); border:1px solid var(--line-inset);
  border-radius:13px; padding:0; box-shadow:none; }
/* A REAL 15px LANE, on the LIST — which is the correction to my own last pass. That one put the 15
   on the row's PADDING to keep the reference's full-width separators, and it moved the buttons out
   from under the bar but left the row BOX running to the edge: the whole row is a click target
   (tapping it auditions the track), so a click in the lane still scrolled instead of playing.
   The row box has to move, and the separator has to stay full width. Both, and neither is a
   compromise: the list carries the lane, and the separator is a ::before that spans back across it
   — right:-15px lands exactly on the list's padding edge, so it adds no scrollable overflow.
   Cost, stated: the auditioning row's highlight now stops at the lane rather than at the window
   edge. That is what a lane IS. */
#viewPanel .st-tkList{ padding:0 15px 0 0; }
#viewPanel .st-tkList .st-tkRow{ position:relative; min-height:44px; height:auto; box-sizing:border-box;
  padding:10px 12px; gap:var(--sp-close); border-bottom:none; }
/* separator BETWEEN rows only — the first has none, exactly as the reference measures */
#viewPanel .st-tkList .st-tkRow + .st-tkRow{ border-top:none; }
#viewPanel .st-tkList .st-tkRow + .st-tkRow::before{ content:''; position:absolute; pointer-events:none;
  left:0; right:-15px; top:0; border-top:1px solid var(--line-inset); }
/* the handle is the ONE treatment the whole product uses (see .sv-drag/.sv-subDrag) */
/* (ds) the panel's handle is on the shared rule now — see .sv-drag/.sv-subDrag/.st-tkDrag */
#viewPanel .st-tkNum{ font-family:Inter,-apple-system,sans-serif; font-size:var(--t-panellabel);
  font-weight:var(--t-w-reg); letter-spacing:normal; color:var(--txt-muted);
  width:calc(var(--num-ch, 2) * 1ch); text-align:right; font-variant-numeric:tabular-nums; }
#viewPanel .st-tkName{ font-family:Inter,-apple-system,sans-serif; font-size:var(--t-rowtitle);
  font-weight:var(--t-w-semi); letter-spacing:var(--t-tracking); color:var(--ink-value); }
#viewPanel .st-tkDur{ font-family:Inter,-apple-system,sans-serif; font-size:var(--t-panellabel);
  font-weight:var(--t-w-reg); letter-spacing:normal; color:var(--ink-quiet); }
#viewPanel .st-tkEdit svg{ width:12.5px; height:12.5px; }
#viewPanel .st-tkX svg{ width:14px; height:14px; }

/* fields: filled inset grounds, not translucent white */
#viewPanel .st-namePrompt{ font-size:var(--t-panellabel); font-weight:var(--t-w-semi);
  letter-spacing:var(--t-tracking); }
#viewPanel .st-input{ background:var(--surface-inset); border:1px solid var(--line-inset);
  border-radius:12px; padding:14px 15px; letter-spacing:var(--t-tracking); }
#viewPanel #title{ min-height:47px; font-size:var(--t-panelfield); font-weight:var(--t-w-med); color:var(--ink-value); }
#viewPanel #message{ min-height:45px; font-size:var(--t-panelmsg); font-weight:var(--t-w-reg); color:var(--ink-value); }
#viewPanel .st-input::placeholder{ color:var(--ink-quiet); }
/* focus is the RULED blue, the same one every fill uses */
#viewPanel .st-input:focus{ border-color:var(--blue); box-shadow:none; }
#viewPanel .st-flabel{ font-size:var(--t-panellabel); font-weight:var(--t-w-reg);
  letter-spacing:var(--t-tracking); color:var(--txt-muted); }
#viewPanel .st-pill{ flex:none; width:82px; height:34px; background:var(--surface-inset);
  border:1px solid var(--line-inset); border-radius:10px; font-size:var(--t-panelurl);
  font-weight:var(--t-w-semi); letter-spacing:var(--t-tracking); color:var(--ink-value); }
#viewPanel .st-pin{ flex:none; width:138px; height:38px; background:var(--surface-inset);
  border:1px solid var(--line-inset); border-radius:10px; font-size:var(--t-panelurl);
  font-weight:var(--t-w-reg); color:var(--ink-value); }
#viewPanel #dlNote{ font-size:var(--t-panellabel); font-weight:var(--t-w-reg);
  letter-spacing:var(--t-tracking); }
/* auto-play label: the reference reads 12/400 #8E9196, live carried the old 10.5/500 */
/* (moved to sendbox.css — ONE shared control, see the SHARED CONTROLS block there) */

/* the link row. Its BUTTONS moved to the pinned bar; the url readout stays in the body, because it
   is something you READ, not something you do. */
#viewPanel .st-url{ height:40px; background:var(--surface-inset); border:1px solid var(--line-inset);
  border-radius:12px; padding:0 14px; }
#viewPanel #urlText{ font-size:var(--t-panelurl); font-weight:var(--t-w-reg);
  letter-spacing:normal; color:var(--ink-quiet); }

/* ---- THE PINNED ACTION BAR ------------------------------------------------------------------
   THE BAR IS THE CARD'S CHILD, NOT THE PANEL'S. The first build made it sticky inside #viewPanel,
   which forced the SCROLLER to carry a child 44px wider than its own content box, and to get that
   the panel had to bleed too (margin:0 -22px / padding:0 22px). Two faults came back from real use
   and both trace to that one decision:
     · a horizontal scrollbar on the panel at desktop. The construction is flush by ARITHMETIC — with
       a classic 15px bar the scrollport is 395, the content box 351, and the bar's margin box
       351+44 = 395 exactly. Flush is not the same as safe: it is one rounding away from overflow,
       and it cannot be reproduced here, because headless Chrome will not draw a space-taking
       scrollbar at all (::-webkit-scrollbar reserves 0px — measured; --disable-features=
       OverlayScrollbar changes nothing). A construction that survives every measurement I can make
       and still fails on a real machine is one to REMOVE, not to tune.
     · the panel's own vertical scrollbar moved 22px right with its box, out to the card's edge,
       where it reads as a page scrollbar instead of a panel one. That is the "grey scrollbar".
   Out here the bleed lives in .st-card, which is overflow:clip — not a scroll container at all, so
   it can neither draw a bar nor be scrolled programmatically. #viewPanel goes back to exactly the
   box it had before this arc, and so does its scrollbar. The bar still sits at the bottom of the
   card, because it is the last flex child of a column whose #viewPanel is the flexible one — and it
   is pinned by CONSTRUCTION now rather than by sticky, so it cannot drift.
   The bottom -22 is real here (it was inert under sticky): the bar eats 22 of the card's 24px
   bottom padding and lands 2px above the inner edge, which is what the approved crop shows. */
/* BREATHING ROOM ABOVE THE BAR (Sid, 2026-08-02). The url row ended flush against the hairline:
   the panel's last element stopped exactly where the bar began, so a readout and a control edge
   shared one line with nothing between them. On the panel's own padding rather than on any one
   element, because "the last thing before the bar" is a different element in every state — the url
   row on a clean link, the downloads row on create, the receipt in the wizard. A scroll container's
   end padding counts as scrollable, so the last row can still clear the bar completely.
   --sp-block: 18 at desktop, 14 at 390. The fade is 26 and sits over this. */
#viewPanel{ padding-bottom:var(--sp-block); }
.st-card > .st-bar{ flex:0 0 auto; position:relative; z-index:6;
  margin:0 -22px -22px; padding:14px 22px calc(14px + env(safe-area-inset-bottom,0px));
  display:flex; align-items:center; gap:var(--sp-close);
  background:var(--surface-card); border-top:1px solid var(--line-inset); }
/* THE FADE MEANS "THERE IS MORE BELOW", so it is wired to the scroll position and not painted
   permanently — at the end of the panel it says something untrue. studio.js sets .more while the
   panel still has somewhere to go (see syncPanelFade). 26px of the card ground, over the panel's
   last rows, ignoring the pointer. */
.st-card > .st-bar::before{ content:''; position:absolute; left:0; right:0; bottom:100%; height:26px;
  opacity:0; transition:opacity .12s linear;
  pointer-events:none; background:linear-gradient(180deg, rgba(26,27,29,0) 0%, var(--surface-card) 100%); }
.st-card > .st-bar.more::before{ opacity:1; }
.st-barGap{ flex:1 1 auto; }
/* IOS: THE BAR STANDS DOWN WHILE THE KEYBOARD IS UP, on coarse pointers only. The card is capped in
   svh, which the keyboard does not change, so a bar at the card's bottom sits BEHIND the keyboard —
   occupying room the sender cannot see or reach while covering nothing they can. Hiding it gives
   that room back to the field being typed into and brings it straight back when the keyboard goes.
   studio.js decides from visualViewport, NOT from focus: the panel focuses #title itself in the
   separate-links wizard and the name prompt, neither of which opens a keyboard, and a focus-driven
   rule took the wizard's only action off screen.
   (The first build unpinned it instead, so it could be scrolled to; that only worked while it was a
   sticky child of the scroller. It is not, for the reasons above, so this is the honest equivalent
   and it is a deviation from the ruled mechanic, not from its purpose.) */
.st-card > .st-bar.kbd{ display:none; }

.st-card > .st-bar .st-mbtn{ flex:0 0 auto; height:43px; padding:0 18px; border-radius:12px;
  background:var(--surface-inset); border:1px solid var(--line-inset); color:var(--ink-value);
  font-size:var(--t-body); font-weight:var(--t-w-semi); letter-spacing:var(--t-tracking); box-shadow:none; }
/* copy link is the primary on a clean panel: the link exists, handing it over is the next thing */
.st-card > .st-bar .st-mbtn.copy{ min-width:150px; background:var(--blue); border-color:var(--blue); color:#fff; }
/* #delBtn, not .st-delLink: an earlier rule targets the ID and would outrank a class here.
   flex:0 1 with min-width:0 is what lets it WRAP to two lines at 390 instead of shouldering the
   primary off the card — the one element in the bar that is allowed to give. */
#delBtn{ flex:0 1 auto; min-width:0; font-size:var(--t-paneldel); font-weight:var(--t-w-semi);
  letter-spacing:var(--t-tracking); color:var(--red-del); text-align:left; }
/* the two primaries share one slot and one shape — only one is ever on screen */
.st-card > .st-bar #saveBtn, .st-card > .st-bar #goBtn{ flex:0 0 auto; width:auto; min-width:150px;
  height:46px; padding:0 18px; margin-top:0; border-radius:12px;
  font-size:var(--t-panelsave); font-weight:var(--t-w-bold); letter-spacing:var(--t-tracking); }

/* the collection delete dialog: three actions do not fit the two-button row's default sizing, so
   the row wraps and the keep action takes the full width under cancel/delete. Same component,
   same buttons — only the shape of the row changes when the third one is present. */
#delRow.three{ flex-wrap:wrap; row-gap:var(--sp-tight); }
#delRow.three #delKeep{ order:3; flex:1 0 100%; }


/* ---- collection panel, round 2: the six Sid marked (2026-07-31) --------------------------------
   BLOCK RHYTHM. The first pass sized every box in the table and never measured the space BETWEEN
   them, so a 31/31 run could be green while the whole panel read tight. The reference's gaps, on
   declared steps: well -> transport 24, transport -> queue 24, queue header -> panel 12, panel ->
   name label 24, label -> field 12, field -> field 12, message -> the detail rows 30. */
#viewPanel .st-playRow{ margin-top:var(--sp-air); }
/* margin-BOTTOM too: an earlier rule sets `margin:16px 0 18px`, so setting only the top left 18px
   of its bottom stacking under the fields' own 24 and the panel->name gap measured 42. */
#viewPanel .st-tracks{ margin-top:var(--sp-air); margin-bottom:0; }
#viewPanel .st-tkHead{ margin-bottom:var(--sp-close); }
#viewPanel .st-fields{ margin-top:var(--sp-air); }
#viewPanel .st-namePrompt{ margin:0 0 var(--sp-close) 0; }
#viewPanel .st-input + .st-input{ margin-top:var(--sp-close); }
/* THE LOWER REGION runs on --sp-block, all of it. Last pass set 30 here because the measurement
   was taken to the LABEL and the fix applied to the ROW — the label sits inside the row's own
   padding, so anchoring the two to different elements baked in a 12px error, and the harness then
   asserted my number rather than the reference's. Measured row-to-row: 18 throughout. */
#viewPanel #message + .st-detailRow{ margin-top:var(--sp-block); }
#viewPanel .st-detailRow + .st-detailRow{ margin-top:var(--sp-block); }
/* the link row is not a sibling of the detail rows (it lives in #pmReady, outside #pmFields), so
   the gap is its OWN margin — .st-readyUrl's 9, not anything the rows could set */
#viewPanel .st-readyUrl{ margin-top:var(--sp-block); }
/* the expiry pill's VALUE: an earlier #id rule pulls it to --t-body, so the pill's own 14 never
   reached the text inside it */
#viewPanel #expLabelTxt{ font-size:var(--t-panelurl); }
/* the pin is SANS in the reference, not mono — the third time a family has survived because I
   asserted a field's box, radius and alignment and never what it is set in */
#viewPanel .st-pin{ font-family:Inter,-apple-system,sans-serif; }

/* EDGE INSET. Row content sits 9px in from the card's content edge — the same COMPONENT inset the
   studio list already runs on (e09d9ca, ruled deliberately off the 6/12/18/24/30 scale). Full-width
   blocks (the well, the track panel, the fields, the link row) stay flush at the edge; only the
   text rows come in, which is what stops "+ add tracks" and the labels reading as if they had
   fallen off the side. */
/* 8, not 9: these rows sit inside the card's 1px border, so 8 is the DELTA that lands the measured
   9 from the card's content edge. Same lesson as the studio's own 9px inset, which needed
   margin-left:8 for the same reason — the number in the ruling is where the ink ends up, not what
   you type. */
#viewPanel .st-tkHead, #viewPanel .st-detailRow{ padding-left:8px; padding-right:8px; }

/* the url is SANS in the reference, not mono — the first pass asserted its size and colour from
   the table and never its family, which is the same subset-of-what-I-changed failure the sub-row
   mono incident already cost a round. Case preservation is untouched (it is in the exempt list),
   and letter-spacing stays normal: the reference's +0.01em is in Helvetica, and Inter's
   compensation is -0.01em, so the two cancel. */
#viewPanel #urlText{ font-family:Inter,-apple-system,sans-serif; }

/* the pin reads left, like every other field on the card; centring it was a leftover from when it
   was a lone 4-digit box */
#viewPanel .st-pin{ text-align:left; }


/* ---- the TEXT blue, applied by role (Sid, 2026-07-31) -----------------------------------------
   Small blue TEXT takes --blue-text; fills, buttons and focus borders keep --blue. Swept rather
   than matched to the demo: the collection reference only happens to render two of these, but the
   rule is about what a thing IS, not which screenshot showed it. */
#viewPanel .st-tkAdd, #viewPanel .st-namePrompt, #viewPanel #dlNote.on{ color:var(--blue-text); }
.sv-rsplit{ color:var(--blue-text); }


/* ==============================================================================================
   iOS SAFARI: the focused-field shift (Sid's device, 2026-07-31)
   ============================================================================================== */

/* CAUSE 1 — AUTO-ZOOM. WebKit zooms the page whenever a focused text input computes under 16px,
   and leaves it zoomed and pannable afterwards, which is the card sitting off the left edge and
   the page scrolling sideways. The panel's fields are 15.5 / 14.5 / 14, so every one of them
   trips it. The fix is the field's own size on touch pointers only — not a layout change, and
   NOT maximum-scale in the viewport meta, which would disable pinch zoom for everyone.
   text-size-adjust does not help here: it governs text INFLATION on reflow, not the focus zoom.
   Desktop keeps the declared steps; a coarse pointer is the only place this applies. */
@media (pointer:coarse){
  /* by ID, because the panel pass sets these with #viewPanel #title / #message (two IDs) and a
     media query adds NO specificity of its own — the class version of this rule lost silently and
     only the pin, which has no #id rule, actually moved. The harness caught it; the eye would not
     have, because 15.5 and 16 look identical and the zoom is the only tell. */
  #viewPanel #title, #viewPanel #message, #viewPanel #pin,
  #viewPanel .st-input, #viewPanel .st-pin, .acmodal .acinput, .acmodal .modalinput{
    font-size:16px;
  }
}

/* CAUSE 3, and the one that actually survives typing. #viewPanel has overflow-y:auto, and the
   spec forces overflow-x to auto alongside it — so the panel is a horizontally SCROLLABLE
   container, and WebKit scrolls the nearest scrollable ancestor to keep the caret in view on focus
   AND on every keystroke. That is why the card slid off to the left and STAYED there after typing,
   and why the 16px font fix alone did not stop it.
   Measured, not guessed. The transport's natural width is 38 spacer + 44 prev + 84 play + 44 next
   + 38 auto + four 18px gaps = 320, against 301 of panel content at 375 and 316 at 390. A centred
   flex row that overflows spills EQUALLY both ways, so half of that 19px went past the right edge
   and became scrollable width. The label adds to it: this surface renders it at 12px (the
   collection pass took it up from 10.5), so it measures 52.3px inside a box that the shared rule
   sized 38px for a 36.2px label at 8px — and being absolutely positioned and centred, it hangs
   ~7px past the box on each side.
   Two earlier attempts here were inert and are recorded so they are not tried again: `width:max-content`
   on the cluster, which loses to `flex:0 0 38px` because flex-basis beats width for a flex item;
   and `overflow-x:clip` on the panel, which the spec degrades to `hidden` when overflow-y is auto,
   and a hidden box is still programmatically scrollable — exactly the trick WebKit is using. The
   only fix that holds is removing the overflow itself.
   So, phone and panel only — the desktop cluster, the collection view and the player are untouched:
   the transport gap drops one declared step, which brings the row to 284 at 390 and fits 375 with
   room; and the label is anchored to its box's right edge rather than centred on it, so the
   rightmost element in the row cannot hang past the card. The label sits ~7px left of the toggle's
   centreline as a result; that is deliberate, it aligns to the row's right edge instead. */
@media (max-width:480px){
  #viewPanel .st-playRow{ gap:var(--sp-close); }
}

/* CAUSE 2 — a genuine overflow I introduced. The link-row actions took the reference's fixed
   252 + 102 from a 412 card; at 390 the card is 362 wide with ~318 of content, so the pair
   overran and .st-card's overflow:hidden CLIPPED them rather than scrolling. The fixed widths are
   gone with the old two-button row; what remains is the same lesson applied to the bar — at 390 it
   carries THREE things on one line and the phone is where it has to give.
   WHO GIVES, in order: the secondary loses its side padding first, then "delete link" wraps to two
   lines (which is what the approved 390 crop shows), and the primary never shrinks below the width
   its label needs. The primary is the last thing on the card that should be squeezed. */
@media (max-width:480px){
  .st-card > .st-bar{ gap:var(--sp-tight); }
  .st-card > .st-bar .st-mbtn.share{ padding-left:14px; padding-right:14px; }
  .st-card > .st-bar .st-mbtn.copy{ min-width:0; }
  .st-card > .st-bar #saveBtn, .st-card > .st-bar #goBtn{ min-width:0; }
}

/* SCROLLBARS ON TOUCH. Desktop deliberately carries NO scrollbar styling on the studio list and
   the expansion window (see the rules by .st-list and .sv-sub): the reference styles none, and
   that absence is precisely what buys the OS overlay bar — dark, and hidden at rest. Any
   ::-webkit-scrollbar rule switches those elements to always-visible custom bars, so the desktop
   treatment must stay untouched. On a phone the same unstyled path renders a standing grey bar
   instead, which is what Sid flagged. A finger is its own affordance there, so on coarse pointers
   the bars are removed outright — the same treatment .st-tkList already carries everywhere.
   Scoped to (pointer:coarse) so nothing about the desktop overlay changes. */
@media (pointer:coarse){
  .st-list, .sv-sub, .st-tkList, #viewPanel, .st-card, .st-sepList{ scrollbar-width:none; }
  .st-list::-webkit-scrollbar, .sv-sub::-webkit-scrollbar, .st-tkList::-webkit-scrollbar,
  #viewPanel::-webkit-scrollbar, .st-card::-webkit-scrollbar, .st-sepList::-webkit-scrollbar{
    width:0; height:0; }
}
