/* vocal-gpu-studio.css — the Vocal Remover STUDIO, /vocalremover-gpu ONLY.
 *
 * Its own sheet, linked only by vr-gpu.php: css/vocal-combined.css is shared with the public tool
 * and the separation law of 2026-09-21 keeps this page's surfaces out of it. Colours come from the
 * cosmic tokens, so both themes and every palette follow automatically.
 *
 * Layout rules this follows, rather than one breakpoint's taste:
 *  - Desktop keeps stage and voice desk side by side; the page hides its side rails in this view so
 *    nothing needs horizontal scrolling.
 *  - Tablet defers the compact view for as long as possible: the same pieces restack, the channel
 *    strips scroll horizontally with snap instead of turning into a different control.
 *  - Phone puts the stage first because it is the point of the screen, and turns the vertical
 *    faders horizontal — a 12 mm-wide vertical fader cannot be dragged accurately with a thumb.
 *  - Targets are >= 44px with real padding on touch, >= 24px on pointer.
 *  - Nothing states meaning by colour alone; M and S carry their letters and aria-pressed.
 */

.st {
  /* --panel / --stroke / --hover were consumed ~10 times in this sheet and defined NOWHERE in the
     repo, so every one of them always painted its hardcoded rgba(255,255,255,...) fallback — which
     is invisible on the light theme. They are real tokens now, derived from the cosmic set that
     cssVars() actually writes. --stem-6 is the same story: cssVars stops at --stem-5 and the guitar
     strip was falling back to a hardcoded amber. */
  --panel: var(--surface, rgba(255, 255, 255, .04));
  --stroke: var(--line, rgba(255, 255, 255, .14));
  --hover: color-mix(in srgb, var(--ink, #fff) 8%, transparent);
  --stem-6: var(--accent2, var(--accent, #7c5cff));
  /* A raised opaque sheet for panels that float over content. --card is NOT a token in this design
     system (probed on staging, light and dark), so anything reaching for it silently took its
     hardcoded fallback — which is how the effect rack came out dark-on-dark on the light theme. */
  --st-sheet: color-mix(in srgb, var(--bg, #0b0a1a) 94%, var(--ink, #fff) 6%);

  /* THE HEIGHT BUDGET. The desk is a FIXED box that both mixer styles live inside, which is what
     makes switching between them free of layout shift — and what keeps a seven-track job from
     pushing the page down. Measured on this page: top bar 82, gaps 2x, and the stage used to take
     460 on its own. */
  --st-gap: clamp(10px, 1.1vw, 15px);
  --st-radius: 18px;
  --st-stage-h: clamp(236px, 30vh, 330px);
  --st-voice-min: 306px;
  --st-main-h: max(var(--st-stage-h), var(--st-voice-min));
  /* A MINIMUM, not a height. The first version fixed the row at 62px, which is exactly the knob's
     height on a fine pointer (dial 40 + 2 margin + 10.5 value + 9.5 label) with the 2px border
     forgotten — and `@media (pointer: coarse)` then grows the dial to 44px, making the knob 66px.
     So the labels painted outside the row on every touch device, and on a phone the whole control
     line landed 15px inside the NEXT track's card. Heights that content can exceed are the bug;
     a minimum plus auto height cannot have it. */
  --st-row-h: 80px;
  --st-desk-h: clamp(268px, calc(100dvh - 82px - var(--st-main-h) - 84px), 520px);
  display: grid;
  gap: var(--st-gap);
}

/* The two surfaces that are PORTALLED to <body> — the full-screen stage and the lyrics editor — live
   outside .st, so the tokens above do not reach them: the editor came out see-through on a phone
   (measured 2026-09-24, the page text showing through every line), and the Style panel would have
   done the same in full screen. Same values, declared where the portals can see them. */
.st-stage-full,
.vst-ed-wrap,
.st-vc-wrap {
  --panel: var(--surface, rgba(255, 255, 255, .04));
  --stroke: var(--line, rgba(255, 255, 255, .14));
  --hover: color-mix(in srgb, var(--ink, #fff) 8%, transparent);
  --st-sheet: color-mix(in srgb, var(--bg, #0b0a1a) 94%, var(--ink, #fff) 6%);
}

/* ── top bar ─────────────────────────────────────────────────────────────────────────────── */
.st-bar {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(280px, 2fr) auto;
  align-items: center;
  gap: var(--st-gap);
  padding: clamp(10px, 1.4vw, 16px) clamp(12px, 1.6vw, 20px);
  border: 1px solid var(--stroke, rgba(255, 255, 255, .14));
  border-radius: var(--st-radius);
  background: var(--panel, rgba(255, 255, 255, .04));
}

.st-title {
  margin: 0;
  font-size: clamp(15px, 1.5vw, 19px);
  font-weight: 600;                      /* never a light weight: it is the loudest label here */
  line-height: 1.25;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.st-sub {
  margin: 2px 0 0;
  font-size: 12.5px;
  opacity: .8;                           /* stays above the 4.5:1 floor on both themes */
}

.st-transport {
  display: flex;
  align-items: center;
  gap: 10px;
  min-width: 0;
}

.st-play {
  flex: none;
  width: 48px;
  height: 48px;
  display: grid;
  place-items: center;
  border: 0;
  border-radius: 50%;
  background: var(--cta, linear-gradient(135deg, #4f7cff, #a35bff));
  color: var(--accent-ink, #fff);
  cursor: pointer;
}

.st-play:focus-visible,
.st-icon-btn:focus-visible,
.st-rec:focus-visible,
.st-chip:focus-visible,
.st-ms:focus-visible,
.st-cta:focus-visible { outline: 2px solid var(--accent, #7c5cff); outline-offset: 2px; }

.st-time { font-size: 12.5px; opacity: .85; font-variant-numeric: tabular-nums; }
/* an iPhone loads the tracks into memory before they play (vocal-buftrack.jsx): the Play button says so */
.st-transport:has(.st-buf-load) { flex-wrap: wrap; row-gap: 4px; }
.st-transport:has(.st-buf-load) .st-scrub { flex-basis: 0; }   /* the bar gives way: only the message takes line 2 */
.st-buf-load { flex-basis: 100%; font-size: 12px; opacity: .8; }
.st-play.is-loading { animation: st-buf-pulse 1.2s ease-in-out infinite; }
@keyframes st-buf-pulse { 50% { opacity: .55; } }
@media (prefers-reduced-motion: reduce) { .st-play.is-loading { animation: none; opacity: .7; } }
.st-scrub { flex: 1 1 auto; min-width: 80px; height: 32px; }

.st-bar-actions { display: flex; flex-wrap: wrap; gap: 8px; justify-content: flex-end; }

/* ── main split ──────────────────────────────────────────────────────────────────────────── */
.st-main {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(320px, 400px);
  gap: var(--st-gap);
  align-items: stretch;
}

.st-stage,
.st-voice,
.st-desk {
  border: 1px solid var(--stroke, rgba(255, 255, 255, .14));
  border-radius: var(--st-radius);
  background: var(--panel, rgba(255, 255, 255, .04));
}

/* A FIXED height, not a minimum: the stage is a screen inside the studio and must never grow the
   page. Every template - list included - scrolls inside it. It gave up roughly 130px on 2026-09-23
   to pay for horizontal track rows; the karaoke text scales to whatever it is given. */
.st-main { height: var(--st-main-h); }
.st-stage {
  position: relative;
  display: flex;
  flex-direction: column;
  height: 100%;
  min-height: 0;
}

.st-stage-head,
.st-voice-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
  padding: 12px clamp(12px, 1.6vw, 18px) 0;
}

.st-stage-head h3,
.st-voice-head h3 { margin: 0; font-size: 14px; font-weight: 600; letter-spacing: .02em; }
.st-stage-head-r { display: flex; align-items: center; gap: 8px; }

.st-badge-float {
  position: absolute;
  z-index: 5;
  top: 14px;
  inset-inline-end: 56px;               /* clear of the floating full-screen button */
}

.st-badge {
  padding: 3px 8px;
  border-radius: 999px;
  border: 1px solid var(--stroke, rgba(255, 255, 255, .18));
  font-size: 11.5px;
  opacity: .85;
}

.st-icon-btn {
  width: 36px; height: 36px;
  display: grid; place-items: center;
  border: 1px solid var(--stroke, rgba(255, 255, 255, .18));
  border-radius: 10px;
  background: transparent;
  color: inherit;
  cursor: pointer;
}

/* Arabic on the stage — Cairo, self-hosted (webfonts/cairo-arabic-var.woff2, 30 KB, SIL OFL 1.1,
   licence beside the file). Until now Arabic fell back to whatever the device had: correct, but
   never chosen, and at karaoke size that shows. ONE variable file covers every weight we use.

   The unicode-range is the point: the browser downloads this only when a page actually paints
   Arabic, so an English lyric never pays for it, and Latin text keeps the site's own face even
   inside a mixed line. */
@font-face {
  font-family: "Cairo Arabic";
  src: url("../webfonts/cairo-arabic-var.woff2") format("woff2");
  font-weight: 200 1000;
  font-style: normal;
  font-display: swap;
  unicode-range: U+0600-06FF, U+0750-077F, U+0870-088E, U+0890-0891, U+0897-08E1, U+08E3-08FF,
                 U+200C-200E, U+2010-2011, U+204F, U+2E41, U+FB50-FDFF, U+FE70-FE74, U+FE76-FEFC;
}
/* The site's Geist under a name of the stage's own, with font-display: SWAP (2026-09-30). The site declares Geist
   `optional`: a page that does not have the file within ~100 ms keeps the fallback for good — and the stage refuses a
   synthesised bold, so its words stayed a thin Arial on such a load (Italian, Portuguese and Russian lyrics in one test
   run of the sixteen languages). Same files as php/fonts-inline.css (so nothing is fetched twice): the stage swaps to
   Geist the moment it arrives. */
@font-face {
  font-family: "MZL Geist"; /* cyrillic-ext */
  src: url(https://fonts.gstatic.com/s/geist/v5/gyByhwUxId8gMEwRGFWfOw.woff2) format('woff2');
  font-weight: 100 900; font-style: normal; font-display: swap;
  unicode-range: U+0460-052F, U+1C80-1C8A, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F;
}
@font-face {
  font-family: "MZL Geist"; /* cyrillic */
  src: url(https://fonts.gstatic.com/s/geist/v5/gyByhwUxId8gMEwYGFWfOw.woff2) format('woff2');
  font-weight: 100 900; font-style: normal; font-display: swap;
  unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
}
@font-face {
  font-family: "MZL Geist"; /* vietnamese */
  src: url(https://fonts.gstatic.com/s/geist/v5/gyByhwUxId8gMEwTGFWfOw.woff2) format('woff2');
  font-weight: 100 900; font-style: normal; font-display: swap;
  unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+0300-0301, U+0303-0304, U+0308-0309, U+0323, U+0329, U+1EA0-1EF9, U+20AB;
}
@font-face {
  font-family: "MZL Geist"; /* latin-ext */
  src: url(https://fonts.gstatic.com/s/geist/v5/gyByhwUxId8gMEwSGFWfOw.woff2) format('woff2');
  font-weight: 100 900; font-style: normal; font-display: swap;
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
@font-face {
  font-family: "MZL Geist"; /* latin */
  src: url(/webfonts/geist-latin.woff2) format('woff2');
  font-weight: 100 900; font-style: normal; font-display: swap;
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

/* ── the karaoke stage ───────────────────────────────────────────────────────────────────────
   The screen is deliberately dark in BOTH themes: a karaoke screen is a lit surface you read from
   across a room, and the accent-on-dark sweep is what makes the sung word obvious at a glance. The
   palette still drives it — every colour below is mixed from the cosmic tokens. */
.vst {
  --vst-scale: 1;
  /* The two states must be unmistakable from across a room: sung is pure white carrying the accent
     glow, unsung is clearly held back. An earlier pair (white .62 against a lavender tint) was so
     close that the sweep read as one flat colour and the karaoke effect disappeared. */
  /* --vst-ink is the colour the singer picks (white by default, owner 2026-09-24). Every other
     shade of the lyric is MIXED from it, so a gold lyric has gold unsung words too, not white ones. */
  --vst-ink: #ffffff;
  --vst-dim: color-mix(in srgb, var(--vst-ink) 38%, transparent);
  --vst-lit: var(--vst-ink);
  --vst-dir: 90deg;
  /* Glow (rebuilt 2026-09-26, owner: "doesn't seem to do anything"): the old halo was the ACCENT on a
     screen painted from the same accent, so on the default background it all but vanished. It is now
     a tight halo in the lyric's own colour plus a bloom mixed from lyric colour and accent, which
     reads on every background, and only the line being sung carries it. */
  --vst-glow-c: color-mix(in srgb, var(--vst-ink) 45%, var(--accent, #7c5cff));
  /* The screen's looks (owner 2026-09-26): ONE property each, used by the screen and by its preview
     tile in the Style panel, so a tile always shows exactly the screen it picks. All dark by design. */
  --vst-bg-default:
    radial-gradient(120% 90% at 50% 0%, color-mix(in srgb, var(--accent, #7c5cff) 26%, transparent), transparent 70%),
    linear-gradient(162deg, #170f38 0%, #241653 42%, #101d47 100%);
  --vst-bg-black: #000;
  --vst-bg-stars: radial-gradient(130% 90% at 50% 115%, #1c2658 0%, #0b1030 52%, #03050d 100%);
  --vst-bg-sunset:
    radial-gradient(90% 55% at 50% 108%, rgba(255, 150, 90, .42), transparent 70%),
    linear-gradient(180deg, #150a2b 0%, #37113f 46%, #661d3c 78%, #8c3130 100%);
  --vst-bg-ocean:
    radial-gradient(80% 60% at 30% 115%, rgba(64, 224, 208, .26), transparent 70%),
    radial-gradient(70% 50% at 80% -10%, rgba(92, 180, 255, .2), transparent 70%),
    linear-gradient(172deg, #021a2c 0%, #04304a 48%, #07465a 100%);
  --vst-bg-stage:
    radial-gradient(70% 38% at 50% 104%, rgba(255, 206, 140, .24), transparent 72%),
    linear-gradient(180deg, #07050b 0%, #0f0a14 62%, #1a1017 100%);
  flex: 1 1 auto;
  display: flex;
  flex-direction: column;
  gap: 10px;
  padding: clamp(10px, 1.2vw, 14px);
  min-height: 0;
}
/* The hand-over (owner 2026-09-29: "like each line is handing the other through a movement, not just a
   hard cut"). Two tempos: RELAY when there is a silence before the next line — the sung line steps aside
   first (lead), the next follows it up and lands soft — and TIGHT for lines back to back, short lines, a
   step back or a seek: every mover on one quicker curve. The lead curve is always at or ahead of the
   follow curve (checked numerically for every t), so two moving lines never collide. */
@property --vst-dim-k { syntax: '<number>'; inherits: true; initial-value: .38; }
.vst {
  --vst-t-lead: 400ms;   --vst-ease-lead: cubic-bezier(.3, .55, .25, 1);
  --vst-t-follow: 460ms; --vst-ease-follow: cubic-bezier(.36, .2, .16, 1);
  --vst-t-tight: 360ms;  --vst-ease-tight: cubic-bezier(.25, .5, .2, 1);
  --vst-mv-in: var(--vst-t-follow); --vst-ease-in: var(--vst-ease-follow);
  --vst-mv-out: var(--vst-t-lead);  --vst-ease-out: var(--vst-ease-lead); --vst-in-delay: 220ms;
}
.st-stage-full .vst { --vst-t-lead: 440ms; --vst-t-follow: 480ms; --vst-t-tight: 380ms; }
.vst-rail[data-tempo="tight"], .vst-list[data-tempo="tight"] {
  --vst-mv-in: var(--vst-t-tight); --vst-ease-in: var(--vst-ease-tight);
  --vst-mv-out: var(--vst-t-tight); --vst-ease-out: var(--vst-ease-tight); --vst-in-delay: 160ms;
}
/* nothing drifts while the song is paused: the aurora restyled the page 238 times a second, even stopped */
.vst:not(.is-playing) .vst-aurora i, .vst:not(.is-playing) .vst-stars i,
.vst:not(.is-playing) .vst-beams i { animation-play-state: paused; }

.vst-rtl { --vst-dir: 270deg; }
/* Arabic sits taller than Latin at the same size: its descenders collide at a 1.24 line-height,
   and the sung/unsung gradient makes any clipping obvious. */
.vst-rtl .vst-line { line-height: 1.5; letter-spacing: 0; }

.vst-screen {
  /* on the whole screen, not just the lyric: the now-playing title is Arabic on an Arabic track
     too. The unicode-range keeps Latin on the site's own face, so a mixed title stays consistent. */
  font-family: var(--vst-font, "Cairo Arabic", "MZL Geist", var(--font-ui, system-ui), sans-serif);
  /* Never fake a bold: Lalezar has one weight, and a synthesised bold of a display face smears. */
  font-synthesis-weight: none;
  position: relative;
  flex: 1 1 auto;
  min-height: 0;
  display: flex;
  flex-direction: column;
  --vst-r: 16px;
  border-radius: var(--vst-r);
  overflow: hidden;
  /* The rounded corner is also a CLIP-PATH (owner 2026-09-30: "the narrow top seek bar makes a visual glitch extending
     outside the frame at each line end"). overflow + border-radius is a clip the GPU compositor can lose for a layer of
     its own for a frame — the progress line, the aurora (1.4x the screen, blurred), a glow that switches at a line
     change — and that layer then shows square past the corner. clip-path is applied to every layer inside. */
  clip-path: inset(0 round var(--vst-r));
  isolation: isolate;
  color: #fff;
  background: var(--vst-bg-default);
  box-shadow: inset 0 0 0 1px rgba(255, 255, 255, .07), 0 18px 48px -28px rgba(0, 0, 0, .9);
}
.vst-screen.vst-bg-black { background: var(--vst-bg-black); }
.vst-screen.vst-bg-stars { background: var(--vst-bg-stars); }
.vst-screen.vst-bg-sunset { background: var(--vst-bg-sunset); }
.vst-screen.vst-bg-ocean { background: var(--vst-bg-ocean); }
.vst-screen.vst-bg-stage { background: var(--vst-bg-stage); }
.vst-screen.vst-bg-custom { background: var(--vst-bgc, #1b1446); }

/* Starfield: three layers of stars tiled at different sizes, twinkling out of step */
.vst-stars { position: absolute; inset: 0; z-index: 0; pointer-events: none; }
.vst-stars i { position: absolute; inset: 0; display: block; background-repeat: repeat; }
.vst-stars i:nth-child(1) {
  background-image:
    radial-gradient(1px 1px at 12% 18%, rgba(255, 255, 255, .7) 50%, transparent 51%),
    radial-gradient(1px 1px at 47% 62%, rgba(255, 255, 255, .6) 50%, transparent 51%),
    radial-gradient(1px 1px at 81% 34%, rgba(255, 255, 255, .7) 50%, transparent 51%),
    radial-gradient(1px 1px at 29% 86%, rgba(255, 255, 255, .5) 50%, transparent 51%),
    radial-gradient(1px 1px at 66% 8%, rgba(255, 255, 255, .6) 50%, transparent 51%);
  background-size: 140px 110px;
  animation: vst-twinkle 4.5s ease-in-out infinite alternate;
}
.vst-stars i:nth-child(2) {
  background-image:
    radial-gradient(1.5px 1.5px at 22% 42%, #fff 50%, transparent 51%),
    radial-gradient(1.5px 1.5px at 73% 76%, #fff 50%, transparent 51%),
    radial-gradient(1.5px 1.5px at 58% 21%, rgba(210, 225, 255, .95) 50%, transparent 51%);
  background-size: 260px 190px;
  animation: vst-twinkle 6.5s ease-in-out 1.2s infinite alternate;
}
.vst-stars i:nth-child(3) {
  background-image:
    radial-gradient(2px 2px at 36% 30%, #fff 50%, transparent 51%),
    radial-gradient(2px 2px at 84% 64%, rgba(255, 236, 210, .95) 50%, transparent 51%);
  background-size: 420px 300px;
  filter: drop-shadow(0 0 3px rgba(255, 255, 255, .8));
  animation: vst-twinkle 8s ease-in-out 2.4s infinite alternate;
}
@keyframes vst-twinkle { from { opacity: .35; } to { opacity: 1; } }

/* Stage: two warm spotlight cones sweeping slowly from above the screen */
.vst-beams { position: absolute; inset: 0; z-index: 0; pointer-events: none; overflow: hidden; }
.vst-beams i {
  position: absolute; top: -12%; width: 44%; height: 135%; display: block;
  transform-origin: 50% 0;
  background: linear-gradient(180deg, rgba(255, 230, 190, .30), rgba(255, 230, 190, .07) 62%, transparent 88%);
  clip-path: polygon(44% 0, 56% 0, 100% 100%, 0 100%);
  filter: blur(7px);
}
.vst-beams i:nth-child(1) { left: 4%; animation: vst-sway-a 10s ease-in-out infinite alternate; }
.vst-beams i:nth-child(2) { right: 4%; animation: vst-sway-b 12s ease-in-out infinite alternate; }
@keyframes vst-sway-a { from { transform: rotate(-16deg); } to { transform: rotate(9deg); } }
@keyframes vst-sway-b { from { transform: rotate(16deg); } to { transform: rotate(-9deg); } }

/* slow drifting light behind the words */
.vst-aurora { position: absolute; inset: -20%; z-index: 0; filter: blur(46px); opacity: .55; }
.vst-aurora i { position: absolute; display: block; border-radius: 50%; }
.vst-aurora i:nth-child(1) {
  width: 46%; height: 70%; left: 4%; top: -8%;
  background: color-mix(in srgb, var(--accent, #7c5cff) 70%, transparent);
  animation: vst-drift-a 19s ease-in-out infinite alternate;
}
.vst-aurora i:nth-child(2) {
  width: 40%; height: 64%; right: 2%; top: 6%;
  background: color-mix(in srgb, var(--accent2, #4f7cff) 62%, transparent);
  animation: vst-drift-b 23s ease-in-out infinite alternate;
}
.vst-aurora i:nth-child(3) {
  width: 52%; height: 54%; left: 26%; bottom: -14%;
  background: color-mix(in srgb, var(--glow, #a35bff) 48%, transparent);
  animation: vst-drift-c 27s ease-in-out infinite alternate;
}
@keyframes vst-drift-a { to { transform: translate3d(12%, 8%, 0) scale(1.12); } }
@keyframes vst-drift-b { to { transform: translate3d(-10%, 12%, 0) scale(1.08); } }
@keyframes vst-drift-c { to { transform: translate3d(6%, -10%, 0) scale(1.14); } }

/* a faint LED weave, so the surface reads as a screen rather than a gradient */
.vst-grid {
  position: absolute; inset: 0; z-index: 0; opacity: .16; pointer-events: none;
  background-image:
    repeating-linear-gradient(0deg, rgba(255, 255, 255, .5) 0 1px, transparent 1px 4px),
    repeating-linear-gradient(90deg, rgba(255, 255, 255, .35) 0 1px, transparent 1px 4px);
  -webkit-mask-image: radial-gradient(120% 80% at 50% 45%, #000 35%, transparent 78%);
          mask-image: radial-gradient(120% 80% at 50% 45%, #000 35%, transparent 78%);
}

/* The old NOW PLAYING header is gone (owner 2026-09-23). It carried the song title and the clock,
   both of which the studio's own transport already showed two inches above, and it was charging the
   words 67px for them: 29% of the screen on a desktop, 55% on a phone, and at tablet width the
   screen was 87px tall and the header WAS all of it — not one lyric was visible. What is left is a
   3px progress line and the full-screen toggle, and both are overlaid, so the words now get the
   whole screen at every width. */
.vst-edge {
  position: absolute; z-index: 3; top: 0; left: 0; right: 0;
  height: 3px; background: rgba(255, 255, 255, .14);
}
.vst-edge i {
  display: block; height: 100%; width: 100%;
  transform-origin: 0 50%; transform: scaleX(0);          /* the painter writes scaleX, per device pixel */
  background: linear-gradient(90deg, var(--accent2, #4f7cff), var(--accent, #7c5cff));
}
.vst-rtl .vst-edge i { transform-origin: 100% 50%; }
.vst-full {
  position: absolute; z-index: 4; top: 10px; inset-inline-end: 10px;
  width: 32px; height: 32px; display: grid; place-items: center;
  border: 1px solid rgba(255, 255, 255, .22); border-radius: 9px;
  /* Opaque enough to stay legible over a lyric that scrolls under it, faint enough not to compete
     with one. It sits over the words, so it cannot be transparent. */
  background: color-mix(in srgb, #0d0a24 62%, transparent);
  -webkit-backdrop-filter: blur(6px); backdrop-filter: blur(6px);
  color: #fff; cursor: pointer; opacity: .72; transition: opacity .15s;
}
.vst-full:hover, .vst-full:focus-visible { opacity: 1; background: color-mix(in srgb, #0d0a24 82%, transparent); }
.vst-full:focus-visible { outline: 2px solid var(--vst-lit); outline-offset: 2px; }

/* Full screen hides the page, and with it the transport — so the stage grows its own. Without it
   there was no pause, no scrub and no visible way back: the only exit was the Escape key, and
   nothing on screen said so. */
.vst-nav {
  display: flex; align-items: center; gap: 10px; flex-wrap: wrap;
  padding: 8px 2px 0;
}
.vst-nav-play {
  width: 46px; height: 46px; flex: none;
  display: grid; place-items: center;
  border: 0; border-radius: 50%; cursor: pointer;
  color: var(--accent-ink, #fff);
  background: var(--cta, linear-gradient(135deg, #4f7cff, #a35bff));
}
.vst-nav-t { font-size: 12.5px; opacity: .85; font-variant-numeric: tabular-nums; }
.vst-nav-scrub { flex: 1 1 200px; min-width: 120px; height: 32px; }
.vst-nav-exit {
  flex: none; min-height: 40px; padding: 0 16px;
  border: 1px solid var(--stroke, rgba(255, 255, 255, .28)); border-radius: 999px;
  background: transparent; color: inherit; font: inherit; font-size: 13px; font-weight: 600;
  cursor: pointer;
}
.vst-nav-exit:hover { background: var(--hover, rgba(255, 255, 255, .1)); }
.vst-nav-play:focus-visible,
.vst-nav-exit:focus-visible { outline: 2px solid var(--accent, #7c5cff); outline-offset: 2px; }

/* the words */
.vst-frame {
  position: relative; z-index: 2;
  flex: 1 1 auto; min-height: 0;
  display: grid;
  align-content: center;
  justify-items: center;
  gap: clamp(4px, .8vw, 10px);
  padding: 6px clamp(10px, 2vw, 26px) 14px;
  text-align: center;
  --vst-fs-cur: calc(clamp(26px, 4.4vw, 54px) * var(--vst-scale));
  --vst-fs-side: calc(clamp(15px, 1.9vw, 24px) * var(--vst-scale));
}
/* Fixed rows: the lines move between their slots, so handing over to the next line moves no layout. */
.vst-frame-flow { grid-template-rows: minmax(0, .8fr) minmax(0, 1.6fr) minmax(0, 1fr); }
.vst-frame-spotlight { grid-template-rows: minmax(0, 1.7fr) minmax(0, 1fr); }
/* Karaoke bar: the bar sits HIGHER, with a band reserved beneath it for the count-in, so the dots
   appear in space that was already there — they used to add a grid row, and the whole block
   jumped up when they came and back down when they went (owner 2026-09-24). */
.vst-frame-bar { grid-template-rows: minmax(0, 1fr) auto; padding-bottom: 38px; }

/* a slot clips rather than lets its line ride over the one above; with --fit that should never
   actually be reached, but a mis-measured font must not produce overlapping text */
.vst-slot { display: grid; place-items: center; width: 100%; min-height: 0; overflow: hidden; }
.vst-frame-bar .vst-slot > * { animation: vst-in .34s cubic-bezier(.22, .9, .3, 1) both; }
@keyframes vst-in { from { opacity: 0; transform: translateY(14px); } to { opacity: 1; transform: none; } }

.vst-line {
  --fit: 1;
  max-width: 100%;
  line-height: 1.24;
  font-weight: var(--vst-wt, 700);
  letter-spacing: -.005em;
  cursor: pointer;
  /* In the frame templates a lyric occupies ONE row: the measured --fit shrinks it until it fits,
     so there is nothing to wrap and nothing for a slot to clip. At one instant of one song the old
     character-count guess dropped a word entirely in Flow and cut through the glyphs in Spotlight
     while List and Karaoke bar were correct. */
  white-space: nowrap;
  overflow: hidden;
  /* the unsung shade, mixed from the ink by a NUMBER so it can fade as a line changes role; the fallback
     keeps unsung words dim where @property is missing */
  --vst-dim: color-mix(in srgb, var(--vst-ink) calc(var(--vst-dim-k, .38) * 100%), transparent);
}

/* The List template is a reading view, not a stage: there a long line may wrap. */
.vst-list .vst-line { white-space: normal; overflow: visible; text-wrap: balance; }

/* ONE layer per word. The sweep is a hard-stop gradient clipped to the glyphs — never a second
   copy of the text on top of the first, which bevelled and ghosted every Arabic line. */
/* A word that is wholly sung, or not yet touched, is painted flat: no gradient means no edge to
   antialias, which is what lit the first letter of an unsung Arabic word. Only the word being sung
   (data-s="mid", written by the painter) carries the hard-stop gradient. */
.vst-w { color: var(--vst-dim); -webkit-text-fill-color: currentColor; }
.vst-w[data-s="on"] { color: var(--vst-lit); }
.vst-w[data-s="mid"] {
  background-image: linear-gradient(var(--vst-dir),
    var(--vst-lit) 0 var(--p, 0%), var(--vst-dim) var(--p, 0%) 100%);
  -webkit-background-clip: text;
          background-clip: text;
  color: transparent;
  -webkit-text-fill-color: transparent;
}

.vst-frame-bar .vst-slot-cur .vst-line { font-size: calc(var(--vst-fs-cur) * var(--fit)); }

/* ── the rail (Flow, Spotlight): the slots are the fixed rows; the lines ride above them ────────── */
.vst-frame-rail .vst-slot { visibility: hidden; pointer-events: none; overflow: visible; }
.vst-frame-rail .vst-slot-cur { font-size: var(--vst-fs-cur); }
.vst-frame-rail .vst-slot-prev, .vst-frame-rail .vst-slot-next { font-size: var(--vst-fs-side); }
.vst-frame-flow .vst-slot-prev { width: calc(100% - 84px); }         /* clear of the full-screen button */
.vst-frame-rail .vst-countin { z-index: 2; }
.vst-rail { position: absolute; inset: 0; z-index: 1; pointer-events: none; }
.vst-rail > .vst-line {
  --y: var(--y-cur, 0px); --s: var(--s-cur, 1);
  position: absolute; left: 50%; top: 0;
  width: max-content; max-width: none; overflow: visible;             /* laid out ONCE, big, on one row */
  font-size: var(--vst-fs-cur);
  transform-origin: 50% 50%;
  transform: translate(-50%, -50%) translateY(var(--y)) scale(var(--s));
  pointer-events: auto;
  transition: transform var(--vst-mv-in) var(--vst-ease-in), opacity 320ms cubic-bezier(.4, 0, .2, 1),
              --vst-dim-k 200ms linear;
}
.vst-rail > [data-role="next"] {
  --y: var(--y-next, 0px); --s: var(--s-next, .45); --vst-dim-k: .5;
  transition: transform var(--vst-mv-in) var(--vst-ease-in),
              opacity 240ms cubic-bezier(.4, 0, .2, 1) var(--vst-in-delay),
              --vst-dim-k 200ms linear;
}
.vst-rail > [data-role="in"] {
  --y: var(--y-in, 0px); --s: var(--s-next, .45); --vst-dim-k: .5; opacity: 0; pointer-events: none;
  transition: transform var(--vst-mv-in) var(--vst-ease-in), opacity 150ms ease-out, --vst-dim-k 0s;
}
.vst-rail > [data-role="prev"] {                                /* lit words x .3 = the old 30 % of ink */
  --y: var(--y-prev, 0px); --s: var(--s-prev, .45); opacity: .3;
  transition: transform var(--vst-mv-out) var(--vst-ease-out),
              opacity var(--vst-mv-out) cubic-bezier(.3, 0, .2, 1),
              --vst-dim-k 200ms linear;
}
.vst-rail > [data-role="out"] {
  --y: var(--y-prev, 0px); --s: var(--s-prev, .45); opacity: 0; pointer-events: none;
  transition: transform var(--vst-mv-out) var(--vst-ease-out), opacity 120ms ease-out, --vst-dim-k 0s;
}
.vst-frame-spotlight > .vst-rail > [data-role="out"] { --y: var(--y-out, 0px); --s: var(--s-cur, 1); }
.vst-rail[data-still] > .vst-line { transition: none !important; }

/* GLOW is its own fill style (owner 2026-09-30), not a halo laid over the line. The old one was a filter on the
   whole line that waited for the line to LAND (a transition delay the length of the move), so every new line
   started filling plain and ~0.4 s later the halo snapped on: "a glitch at each new line". Now the glow is IN the
   fill: the painter's data-s / --p / --q, on the same frame as the letters.
   - a sung word (data-s="on", a flat colour): its own text-shadow;
   - the word being sung (data-s="mid", a gradient clipped to the glyphs — a text-shadow cannot show through
     those): a halo-only copy BEHIND it (transparent text + the same text-shadow, from data-t), masked at the
     fill's edge. Its baseline is PLACED, never left to line-height: normal — that grows with every font the
     copy uses (Cairo's Arabic metrics), and sank the copy under the letters as a doubled Arabic word.
   - unsung words: none (a halo round dim text read as ghosting).
   Only the line being sung glows; it scales with the rail line, so --glow-k (1 / its scale) keeps it the same size. */
:is(.vst-glow .vst-rail > [data-role="cur"], .vst-glow .vst-frame-bar .vst-line,
    .vst-glow .vst-list .vst-line[data-r="0"])
  .vst-w[data-s="on"] {
  text-shadow: 0 0 calc(3px * var(--glow-k, 1)) color-mix(in srgb, var(--vst-ink) 85%, transparent),
               0 0 calc(16px * var(--glow-k, 1)) var(--vst-glow-c);
}
:is(.vst-glow .vst-rail > [data-role="cur"], .vst-glow .vst-frame-bar .vst-line,
    .vst-glow .vst-list .vst-line[data-r="0"])
  .vst-w[data-s="mid"] { position: relative; z-index: 0; }
:is(.vst-glow .vst-rail > [data-role="cur"], .vst-glow .vst-frame-bar .vst-line,
    .vst-glow .vst-list .vst-line[data-r="0"])
  .vst-w[data-s="mid"]::before {
  /* padded round the word so the mask does not cut the halo at the copy's own box */
  --gp: calc(26px * var(--glow-k, 1));
  --front: calc(var(--gp) + (100% - 2 * var(--gp)) * var(--q, 0));
  content: attr(data-t);
  content: attr(data-t) / "";                         /* never read aloud twice */
  position: absolute; z-index: -1; left: calc(-1 * var(--gp)); right: calc(-1 * var(--gp));
  /* the baseline ON the word's, for any face: at line-height 0 a line's baseline sits (A - D) / 2 below its top
     (a fixed line-height counts the first font's A and D only), and the word's box is A + D tall with its
     baseline A down, so a zero-height line at 50 % of that box lands exactly on it. 1em of padding keeps the
     glyphs and their blur inside the mask. */
  top: calc(50% - var(--gp) - 1em); padding: calc(var(--gp) + 1em) var(--gp);
  line-height: 0; white-space: pre; pointer-events: none;
  background: none; color: transparent; -webkit-text-fill-color: transparent;
  text-shadow: 0 0 calc(3px * var(--glow-k, 1)) color-mix(in srgb, var(--vst-ink) 85%, transparent),
               0 0 calc(16px * var(--glow-k, 1)) var(--vst-glow-c);
  --mk: linear-gradient(var(--vst-dir), #000 calc(var(--front) - .04em), transparent calc(var(--front) + .22em));
  -webkit-mask-image: var(--mk); mask-image: var(--mk);
}
/* Karaoke bar: its line clips its overflow (the fit-to-one-row rule), which would cut the words' halo at the box;
   with the glow it paints that far past it (Safari, without overflow-clip-margin, keeps the plain clip) */
.vst-glow .vst-frame-bar .vst-line { overflow: clip; overflow-clip-margin: 26px; }
.vst-glow .vst-bar i { box-shadow: 0 0 14px var(--vst-glow-c); }

/* Always rendered, never in the flow: absolutely placed, and faded in rather than inserted, so its
   arrival can move nothing. */
.vst-countin {
  position: absolute; left: 50%; bottom: 8px; transform: translateX(-50%);
  display: flex; gap: 9px;
  opacity: 0; transition: opacity .2s;
  pointer-events: none;
}
.vst-countin[data-n] { opacity: 1; }
.vst-frame-bar .vst-countin { bottom: 14px; }
.vst-countin i { width: 8px; height: 8px; border-radius: 50%; background: color-mix(in srgb, var(--vst-ink) 30%, transparent); }
.vst-countin[data-n="1"] i:nth-child(3), .vst-countin[data-n="2"] i:nth-child(n+2),
.vst-countin[data-n="3"] i { background: var(--vst-lit); box-shadow: 0 0 10px var(--vst-lit); }

.vst-bar {
  width: min(560px, 92%); height: 6px; justify-self: center;
  border-radius: 999px; background: rgba(255, 255, 255, .18); overflow: hidden;
}
.vst-bar i {
  display: block; height: 100%; width: 100%;
  transform-origin: 0 50%; transform: scaleX(0);          /* the painter writes scaleX, per device pixel */
  background: linear-gradient(90deg, var(--accent2, #4f7cff), var(--vst-lit));
}
.vst-rtl .vst-bar i { transform-origin: 100% 50%; }

/* List: scrolls INSIDE the screen. The stage never grows the page — that was the whole complaint. */
.vst-list {
  position: relative; z-index: 2;
  flex: 1 1 auto; min-height: 0;
  overflow-y: auto;
  overscroll-behavior: contain;
  scrollbar-width: thin;
  /* the TRACK glides (one compositor animation per line change); the box itself jumps, unseen */
  scroll-behavior: auto;
  overflow-anchor: none;
  overflow-x: hidden;
  padding: 0 clamp(10px, 2vw, 26px);
  text-align: center;
  --vst-zoom: 1.28;
  -webkit-mask-image: linear-gradient(to bottom, transparent 0, #000 26px, #000 calc(100% - 26px), transparent 100%);
          mask-image: linear-gradient(to bottom, transparent 0, #000 26px, #000 calc(100% - 26px), transparent 100%);
}
.vst-list::-webkit-scrollbar { width: 6px; }
/* The scrollbar sat on the right, under the full-screen button in the corner (owner 2026-09-24). A
   scroller's bar goes on its inline-START side in right-to-left, so the container is flipped and every
   line flipped back — an Arabic lyric flips back to right-to-left, the direction it came with. */
.vst-list { direction: rtl; scrollbar-color: rgba(255, 255, 255, .28) transparent; }
.vst-list > * { direction: ltr; }
.vst-rtl .vst-list > * { direction: rtl; }
.vst-list::-webkit-scrollbar-thumb { background: rgba(255, 255, 255, .22); border-radius: 999px; }
.st-stage-full .vst-list { --vst-zoom: 1.3; }
@media (max-width: 767px) { .vst-list { --vst-zoom: 1.2; } }
.vst-list .vst-line {                      /* one size, one weight: emphasis is a SCALE, nothing reflows */
  max-width: calc(100% / var(--vst-zoom)); margin-inline: auto; padding: 8px; border-radius: 10px;
  font-size: calc(clamp(14px, 1.6vw, 19px) * var(--vst-scale));
  transform-origin: 50% 50%; --vst-dim-k: .5;
  transition: transform var(--vst-mv-out) var(--vst-ease-out), --vst-dim-k 200ms linear;
}
.vst-list .vst-line[data-r="-1"], .vst-list .vst-line[data-r="-2"] { --vst-dim-k: .34; }
.vst-list .vst-line[data-r="0"] {
  transform: scale(var(--vst-zoom)); --vst-dim-k: .38;
  transition: transform var(--vst-mv-in) var(--vst-ease-in), --vst-dim-k 200ms linear;
}
.vst-list .vst-line[data-r="1"] {
  transition: transform var(--vst-mv-out) var(--vst-ease-out), --vst-dim-k 200ms linear;
}
.vst-list .vst-line:hover { background: rgba(255, 255, 255, .07); }

.vst-tools {
  position: relative;                 /* the Style and download panels are anchored to it */
  display: flex; flex-wrap: wrap; gap: 8px 12px;
  justify-content: space-between; align-items: center;
}
.vst-tpl, .vst-acts { display: flex; flex-wrap: wrap; gap: 6px; }
.vst-act {
  display: inline-flex; align-items: center; justify-content: center; gap: 7px;
  min-height: 34px; padding: 6px 14px;
  border: 1px solid var(--stroke, rgba(255, 255, 255, .18)); border-radius: 999px;
  background: transparent; color: inherit; font: inherit; font-size: 12.5px; font-weight: 600;
  cursor: pointer;
}
/* a long word never squeezes the icon out (Hungarian "Szerkesztés" on a phone, 2026-10-01): the icon keeps its size
   and the label wraps — hyphenated in the page's language where the browser has a dictionary, anywhere as a last
   resort, never over the button's edge */
.vst-act svg { flex: none; }
.vst-act > span { min-width: 0; text-align: center; line-height: 1.15; hyphens: auto; overflow-wrap: anywhere; }
.vst-act:hover { background: var(--hover, rgba(255, 255, 255, .08)); }
.vst-act.on { border-color: var(--accent, #7c5cff); background: color-mix(in srgb, var(--accent, #7c5cff) 14%, transparent); }
/* an edited lyric says so on its own button: a small dot, and never colour alone — the Download panel
   also says "with your edits" */
.vst-act.is-edited::after {
  content: ""; width: 7px; height: 7px; border-radius: 50%;
  background: var(--accent, #7c5cff); margin-inline-start: -2px;
}
.vst-act:focus-visible { outline: 2px solid var(--accent, #7c5cff); outline-offset: 2px; }
.vst-tpl button {
  min-height: 34px;
  padding: 6px 12px;
  border: 1px solid var(--stroke, rgba(255, 255, 255, .18));
  border-radius: 999px;
  background: transparent;
  color: inherit;
  font: inherit;
  font-size: 12.5px;
  font-weight: 500;
  cursor: pointer;
}
.vst-tpl button.on {
  border-color: transparent;
  color: var(--accent-ink, #fff);
  background: var(--cta, linear-gradient(135deg, #4f7cff, #a35bff));
}
.vst-tpl button:focus-visible { outline: 2px solid var(--accent, #7c5cff); outline-offset: 2px; }

/* ── Your video (js/vocal-gpu/vocal-video.jsx, owner 2026-09-29) ─────────────────────────────────
   The stage's fifth tab: the singer's own video plays on the same screen the words do, muted (its
   sound is a track of the studio), in time with the song. The screen stays the stage's fixed size;
   the picture is letterboxed inside it, never cropped. */
.vst-tpl .vst-tpl-video { display: inline-flex; align-items: center; gap: 6px; }
.vst-tpl .vst-tpl-video::before {
  content: ""; width: 14px; height: 14px; flex: none;
  background: currentColor;
  -webkit-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M15 10l5-3v10l-5-3M4 6h11v12H4z'/%3E%3C/svg%3E") center / contain no-repeat;
          mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M15 10l5-3v10l-5-3M4 6h11v12H4z'/%3E%3C/svg%3E") center / contain no-repeat;
}
.vvid-screen { background: #07060d; align-items: center; justify-content: center; }
.vvid-player, .vvid-preview {
  position: absolute; inset: 0; width: 100%; height: 100%;
  object-fit: contain; background: #000; display: block;
}
.vvid-preview { transform: scaleX(-1); }        /* the preview mirrors like a mirror; the file does not */
.vvid-cam { position: absolute; inset: 0; }
/* before the video's own start in the song: its first frame, and when it will begin */
.vvid-wait {
  position: absolute; z-index: 2; inset-inline-start: 50%; bottom: 16px; transform: translateX(-50%);
  padding: 6px 14px; border-radius: 999px; font-size: 12.5px; font-weight: 600;
  background: rgba(0, 0, 0, .62); color: #fff; opacity: 0; transition: opacity .2s ease; pointer-events: none;
  white-space: nowrap;
}
[dir="rtl"] .vvid-wait { transform: translateX(50%); }
.vvid-screen[data-phase="before"] .vvid-wait { opacity: 1; }
.vvid-screen[data-phase="before"] .vvid-player { opacity: .55; }
.vvid-count {
  position: absolute; inset: 0; display: grid; place-items: center; z-index: 2;
  font-size: clamp(64px, 12vw, 140px); font-weight: 800; color: #fff;
  text-shadow: 0 4px 30px rgba(0, 0, 0, .6); background: rgba(0, 0, 0, .25);
}
.vvid-live {
  position: absolute; z-index: 2; top: 12px; inset-inline-start: 12px;
  display: inline-flex; align-items: center; gap: 8px;
  padding: 5px 12px; border-radius: 999px; background: rgba(0, 0, 0, .6); color: #fff;
  font-size: 12.5px; font-weight: 700;
  flex-wrap: wrap; row-gap: 2px; max-width: calc(100% - 76px);   /* clear of the full-screen button, top right */
}
.vvid-live i { width: 9px; height: 9px; border-radius: 50%; background: #ff4d5e; animation: vvid-blink 1.2s steps(2) infinite; }
/* the song has ended: the recording runs on for a few seconds, counted down (owner 2026-09-30) */
.vvid-tail { padding-inline-start: 8px; margin-inline-start: 2px; border-inline-start: 1px solid rgba(255, 255, 255, .3); font-weight: 600; }
.vvid-info { background: rgba(8, 20, 44, .88); color: #dbe7ff; }
@keyframes vvid-blink { 50% { opacity: .25; } }
.vvid-empty {
  position: relative; z-index: 1;
  display: flex; flex-direction: column; align-items: center; gap: 10px;
  max-width: 520px; max-height: 100%; overflow-y: auto; padding: 18px; text-align: center;
  margin-block: auto;                  /* centred while it fits, scrolled from its top when it does not */
}
.vvid-empty h3 { margin: 0; font-size: clamp(16px, 1.6vw, 20px); }
.vvid-empty p { margin: 0; font-size: 13px; line-height: 1.5; opacity: .78; }
.vvid-empty-ic {
  width: 46px; height: 46px; border-radius: 50%; display: grid; place-items: center;
  background: color-mix(in srgb, var(--accent2, var(--accent, #7c5cff)) 24%, transparent);
}
.vvid-empty-ic svg { width: 22px; height: 22px; }
.vvid-row { display: flex; flex-wrap: wrap; justify-content: center; gap: 8px; margin-top: 4px; }
.vvid-row .st-cta { margin-top: 0; display: inline-flex; align-items: center; gap: 8px; }
.vvid-row .vst-act { color: #fff; border-color: rgba(255, 255, 255, .28); }
.vvid-note { font-size: 12px !important; }
/* the Pro lock on Record / Upload / Record again / Replace takes the button's own ink: the site's purple lock
   vanished on the gradient Record button (measured in the screenshot, 2026-09-30), as it did on My voice's CTA.
   Above the site's light-theme lock (:root[data-theme=light] .mzg-lockic, 0-3-0, injected later) — seen losing to it. */
:root .vst.vvid .mzg-lockic { color: inherit; }
.vvid-spin { width: 28px; height: 28px; }
.vvid-bar { width: min(320px, 80%); height: 6px; border-radius: 999px; overflow: hidden; background: rgba(255, 255, 255, .14); }
.vvid-bar i { display: block; height: 100%; background: var(--cta, linear-gradient(135deg, #4f7cff, #a35bff));
              transform-origin: left center; transition: transform .2s linear; }
[dir="rtl"] .vvid-bar i { transform-origin: right center; }
.vvid-err {
  position: absolute; z-index: 3; left: 12px; right: 12px; bottom: 12px; margin: 0;
  padding: 8px 12px; border-radius: 10px; font-size: 12.5px; font-weight: 600; line-height: 1.4;
  background: rgba(40, 6, 12, .88); color: #ffd4d9; text-align: center;
}
.vvid-pick { display: inline-flex; }
.vvid-pick .vh { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; }
.vvid-pick select {
  min-height: 34px; max-width: 200px; padding: 4px 10px;
  border: 1px solid var(--stroke, rgba(255, 255, 255, .18)); border-radius: 999px;
  background: var(--panel, transparent); color: inherit; font: inherit; font-size: 12.5px;
}
.vvid-rec { border-color: color-mix(in srgb, #ff4d5e 60%, transparent); }
.vvid-rec svg, .vvid-stop svg { color: #ff4d5e; }
.vvid-stop { border-color: #ff4d5e; background: color-mix(in srgb, #ff4d5e 16%, transparent); }
.vvid-del { color: color-mix(in srgb, #ff6b7a 80%, var(--ink, #fff)); }
.vst-act:disabled, .vst-tpl button:disabled { opacity: .45; cursor: not-allowed; }
.vvid-hint { margin: 0; font-size: 12px; line-height: 1.45; opacity: .72; }
/* no lyrics: the way to the video sits in the corner of the empty stage, clear of its centred content */
/* no lyrics: the empty stage above, the stage's own tab row under it (Lyrics | Add your video) */
.st-nolyrics-wrap { flex: 1 1 auto; min-height: 0; display: flex; flex-direction: column; }
.st-nolyrics-wrap > .st-nolyrics { min-height: 0; }
.st-nolyrics-tools { padding: 0 clamp(10px, 1.2vw, 14px) clamp(10px, 1.2vw, 14px); }
.st-dl-menu .st-dl-video { border-top: 1px solid var(--stroke, rgba(255, 255, 255, .14)); }
@media (max-width: 760px) {
  .vst-tpl .vst-tpl-video { grid-column: 1 / -1; justify-content: center; }
  /* two tabs only (a song without lyrics): side by side */
  .vst-tpl.vst-tpl-2 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .vst-tpl.vst-tpl-2 .vst-tpl-video { grid-column: auto; }
  .st-nolyrics-wrap > .st-nolyrics { padding: 14px 12px; gap: 8px; }
  .vvid-pick select { max-width: 100%; width: 100%; min-height: 44px; }
  .vvid .vst-acts { grid-auto-flow: row; grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .vvid .vst-acts .vvid-pick, .vvid .vst-acts .vvid-del { grid-column: 1 / -1; }
  /* the phone's screen is short: no icon, a smaller heading, the two ways in side by side */
  .vvid-empty { gap: 8px; padding: 14px 12px; }
  .vvid-empty-ic { display: none; }
  .vvid-empty h3 { font-size: 16px; }
  .vvid-empty p { font-size: 12.5px; line-height: 1.45; }
  .vvid-row { display: grid; grid-template-columns: minmax(0, 1fr); width: 100%; max-width: 320px; }
  .vvid-row .st-cta, .vvid-row .vst-act { min-height: 44px; justify-content: center; font-size: 14px; padding: 8px 14px; }
}

/* ── the Style and download panels ─────────────────────────────────────────────────────────── */
.vst-pop {
  position: absolute; z-index: 30;
  top: calc(100% + 8px); inset-inline-end: 0;
  width: min(360px, 100%);
  max-height: min(70vh, 560px); overflow-y: auto; overscroll-behavior: contain;
  padding: 14px;
  display: grid; gap: 14px;
  border: 1px solid var(--stroke, rgba(255, 255, 255, .16)); border-radius: 16px;
  background: #14121f;
  background: var(--st-sheet, #14121f); color: var(--ink, #f3efff);
  box-shadow: 0 24px 60px -24px rgba(0, 0, 0, .7);
  scrollbar-width: thin;
  scrollbar-color: color-mix(in srgb, var(--ink, #fff) 28%, transparent) transparent;
}
/* in full screen the controls are at the bottom of the window, so the panel opens UP */
.st-stage-full .vst-pop { top: auto; bottom: calc(100% + 8px); }
.vst-pop-sec { display: grid; gap: 8px; }
.vst-pop-row { grid-template-columns: 1fr auto; align-items: center; }
.vst-pop-lbl { margin: 0; font-size: 11.5px; font-weight: 700; letter-spacing: .06em; text-transform: uppercase; opacity: .7; }
.vst-pop-note { margin: 0; font-size: 12px; opacity: .7; line-height: 1.45; }
.vst-pop-note.vst-pop-err { opacity: 1; font-weight: 600; color: color-mix(in srgb, #ff6b7a 85%, var(--ink, #fff)); }
/* the lyric's language, and another one to read it in (owner 2026-09-30) */
.vst-relang-row { display: flex; gap: 8px; align-items: center; flex-wrap: wrap; }
.vst-relang-row select {
  flex: 1 1 140px; min-width: 0; min-height: 40px; padding: 6px 10px;
  border: 1px solid var(--stroke, rgba(255, 255, 255, .18)); border-radius: 10px;
  background: var(--panel, transparent); color: inherit; font: inherit; font-size: 13px;
}
.vst-relang .vh, .vst-relang-row .vh { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; }
.vst-langfix {
  position: absolute; z-index: 4; top: 12px; inset-inline-start: 12px; max-width: calc(100% - 80px);
  min-height: 32px; padding: 5px 12px; border-radius: 999px; cursor: pointer;
  border: 1px solid color-mix(in srgb, #ffd166 60%, transparent);
  background: rgba(0, 0, 0, .55); color: #ffe7a8; font: inherit; font-size: 12.5px; font-weight: 600;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.vst-langfix:hover { background: rgba(0, 0, 0, .7); }
.vst-langfix:focus-visible { outline: 2px solid #ffd166; outline-offset: 2px; }
.lv-render .vst-langfix { display: none !important; }
/* "Sync the lyrics again" for lyrics made before 2026-09-25, first in the Lyrics panel */
.vst-resync {
  display: grid; gap: 8px; margin-bottom: 8px; padding-bottom: 14px;
  border-bottom: 1px solid var(--stroke, rgba(255, 255, 255, .16));
}
.vst-resync .vst-btn { justify-self: start; }
.vst-pop-foot { display: flex; justify-content: flex-end; }
/* Size + Glow wrapper: invisible to a desktop layout (its two rows stay rows of the panel grid);
   the phone block below turns it into one wrapping row */
.vst-pop-pair { display: contents; }
.vst-link { border: 0; background: none; padding: 4px 0; color: var(--accent, #7c5cff); font: inherit; font-size: 12.5px; cursor: pointer; }

/* each font shows ITSELF, set in the lyric's own script */
.vst-fonts { display: grid; grid-template-columns: repeat(auto-fill, minmax(96px, 1fr)); gap: 6px; }
.vst-font {
  display: grid; gap: 2px; justify-items: center;
  padding: 8px 6px; min-height: 58px;
  border: 1px solid var(--stroke); border-radius: 12px;
  background: transparent; color: inherit; cursor: pointer;
}
.vst-font b { font-size: 20px; line-height: 1.25; }
.vst-font span { font-family: var(--font-ui, system-ui), sans-serif; font-weight: 500; font-size: 11px; opacity: .75; }
.vst-font:hover { background: var(--hover); }
.vst-font.on { border-color: var(--accent, #7c5cff); background: color-mix(in srgb, var(--accent, #7c5cff) 14%, transparent); }

/* backgrounds: each tile IS the screen in small, with the lyric colour on it */
.vst-bgs { display: grid; grid-template-columns: repeat(auto-fill, minmax(70px, 1fr)); gap: 6px; }
.vst-bgt {
  position: relative; display: grid; gap: 4px;
  padding: 5px; border: 1px solid var(--stroke); border-radius: 12px;
  background: transparent; color: inherit; font: inherit; cursor: pointer;
}
.vst-bgt:hover { background: var(--hover); }
.vst-bgt.on {
  border-color: var(--accent, #7c5cff);
  background: color-mix(in srgb, var(--accent, #7c5cff) 14%, transparent);
}
.vst-bgt:focus-visible, .vst-bgt-pick:focus-within { outline: 2px solid var(--accent, #7c5cff); outline-offset: 2px; }
.vst-bgt span {
  font-family: var(--font-ui, system-ui), sans-serif; font-weight: 500; font-size: 11px;
  opacity: .75; text-align: center;
}
.vst-sw {
  display: grid; place-items: center; height: 34px; border-radius: 8px;
  font-style: normal; font-weight: 700; font-size: 14px; line-height: 1;
  background: var(--vst-bg-default);
  box-shadow: inset 0 0 0 1px rgba(255, 255, 255, .12);
}
.vst-sw-black { background: var(--vst-bg-black); }
.vst-sw-stars {
  background:
    radial-gradient(1px 1px at 18% 28%, #fff 50%, transparent 51%),
    radial-gradient(1px 1px at 78% 22%, #fff 50%, transparent 51%),
    radial-gradient(1.5px 1.5px at 62% 78%, #fff 50%, transparent 51%),
    radial-gradient(1px 1px at 32% 80%, rgba(255, 255, 255, .7) 50%, transparent 51%),
    var(--vst-bg-stars);
}
.vst-sw-sunset { background: var(--vst-bg-sunset); }
.vst-sw-ocean { background: var(--vst-bg-ocean); }
.vst-sw-stage {
  background:
    linear-gradient(200deg, transparent 38%, rgba(255, 230, 190, .22) 46%, transparent 58%),
    linear-gradient(160deg, transparent 38%, rgba(255, 230, 190, .22) 46%, transparent 58%),
    var(--vst-bg-stage);
}
.vst-bgt-pick input {
  position: absolute; inset: 0; width: 100%; height: 100%;
  opacity: 0; cursor: pointer; border: 0; padding: 0;
}

.vst-colors { display: flex; flex-wrap: wrap; gap: 8px; }
.vst-color {
  position: relative; width: 32px; height: 32px; border-radius: 50%;
  border: 2px solid var(--stroke); background: var(--c); cursor: pointer; padding: 0;
  box-shadow: inset 0 0 0 2px color-mix(in srgb, #000 22%, transparent);
}
.vst-color.on { border-color: var(--accent, #7c5cff); box-shadow: 0 0 0 2px var(--st-sheet), 0 0 0 4px var(--accent, #7c5cff); }
.vst-color-pick {
  background: conic-gradient(#ff5d5d, #ffd166, #7cf5b4, #5ce1ff, #7c5cff, #ff7ab6, #ff5d5d);
  overflow: hidden;
}
.vst-color-pick input { position: absolute; inset: 0; width: 100%; height: 100%; opacity: 0; cursor: pointer; border: 0; padding: 0; }
.vst-color:focus-visible, .vst-color-pick:focus-within { outline: 2px solid var(--accent, #7c5cff); outline-offset: 3px; }

.vst-step { display: inline-flex; align-items: center; gap: 4px; border: 1px solid var(--stroke); border-radius: 999px; padding: 2px; }
.vst-step button {
  min-width: 36px; min-height: 32px; border: 0; border-radius: 999px;
  background: transparent; color: inherit; font: inherit; font-weight: 700; cursor: pointer;
}
.vst-step button:hover { background: var(--hover); }
.vst-step span { min-width: 44px; text-align: center; font-size: 12.5px; }

.vst-switch {
  width: 44px; height: 26px; border-radius: 999px; padding: 3px; cursor: pointer;
  border: 1px solid var(--stroke); background: color-mix(in srgb, var(--ink, #fff) 12%, transparent);
  display: flex;
}
.vst-switch i { width: 18px; height: 18px; border-radius: 50%; background: var(--ink, #fff); opacity: .7; transition: transform .15s; }
.vst-switch.on { background: var(--accent, #7c5cff); border-color: transparent; }
.vst-switch.on i { transform: translateX(18px); background: #fff; opacity: 1; }

.vst-pop-dl { gap: 6px; }
.vst-fmt {
  display: grid; gap: 1px; text-align: start;
  padding: 9px 12px; border: 1px solid transparent; border-radius: 12px;
  background: transparent; color: inherit; font: inherit; cursor: pointer;
}
.vst-fmt:hover, .vst-fmt:focus-visible { background: var(--hover); border-color: var(--stroke); outline: none; }
.vst-fmt b { font-size: 13.5px; }
.vst-fmt b small { margin-inline-start: 6px; font-weight: 500; opacity: .55; }
.vst-fmt span { font-size: 12px; opacity: .72; }

/* ── the lyrics editor ─────────────────────────────────────────────────────────────────────── */
.vst-ed-wrap {
  position: fixed; inset: 0; z-index: 2147483001;     /* above the full-screen stage, which is 2147483000 */
  display: grid; place-items: center; padding: 16px;
  /* the page behind must not compete with the text being edited: darker, and blurred */
  background: rgba(4, 3, 14, .74);
  -webkit-backdrop-filter: blur(6px); backdrop-filter: blur(6px);
}
.vst-ed {
  width: min(720px, 100%); max-height: min(88vh, 900px);
  display: flex; flex-direction: column;
  /* EVERY colour here carries a hard fallback. The owner saw this sheet see-through, with no borders
     and a white native scrollbar — exactly what it looks like when the tokens do not resolve (a tab
     still holding the stylesheet from before they were declared for portals). An editor must never
     depend on that to be legible. */
  border: 1px solid var(--stroke, rgba(255, 255, 255, .16)); border-radius: 18px;
  background: #14121f;
  background: var(--st-sheet, #14121f);
  color: var(--ink, #f3efff);
  box-shadow: 0 30px 80px -30px rgba(0, 0, 0, .8);
  overflow: hidden;
}
.vst-ed-head {
  display: flex; align-items: center; gap: 14px;
  padding: 16px 18px 12px; border-bottom: 1px solid var(--stroke, rgba(255, 255, 255, .16));
}
.vst-ed-title { flex: 1 1 auto; min-width: 0; }
.vst-ed-head h3 { margin: 0 0 4px; font-size: 16px; }
.vst-ed-head p { margin: 0; font-size: 12.5px; opacity: .72; }
/* the song's own clock, to the hundredth: pause on the note, read its exact time */
.vst-ed-transport { flex: none; display: flex; align-items: center; gap: 8px; }
.vst-ed-play {
  width: 40px; height: 40px; display: grid; place-items: center; padding: 0;
  border: 0; border-radius: 50%; cursor: pointer;
  background: var(--cta, linear-gradient(135deg, #4f7cff, #a35bff)); color: var(--accent-ink, #fff);
}
.vst-ed-now { min-width: 64px; font-size: 15px; font-weight: 700; letter-spacing: .02em; }
.vst-ed-list {
  list-style: none; margin: 0; padding: 10px 14px; overflow-y: auto; display: grid; gap: 6px;
  scrollbar-width: thin;
  scrollbar-color: color-mix(in srgb, var(--ink, #fff) 28%, transparent) transparent;
}
/* A row: play-from-here, start – end to the hundredth, the words, and its actions. The times are
   always left-to-right, also inside an Arabic lyric. */
.vst-ed-row {
  position: relative;
  display: grid; align-items: center; gap: 6px 8px;
  grid-template-columns: 36px auto minmax(0, 1fr) 36px;
  grid-template-areas: "hear times text more" "acts acts acts acts" "msg msg msg msg";
  padding: 4px 6px; border-radius: 12px;
}
.vst-ed-row.is-now { background: color-mix(in srgb, var(--accent, #7c5cff) 12%, transparent); }
.vst-ed-hear { grid-area: hear; }
.vst-ed-times { grid-area: times; display: inline-flex; align-items: center; gap: 4px; }
.vst-ed-text { grid-area: text; }
.vst-ed-more { grid-area: more; }
.vst-ed-acts { grid-area: acts; }
.vst-ed-msg { grid-area: msg; }
.vst-ed-hear, .vst-ed-more {
  width: 36px; height: 36px; display: grid; place-items: center; padding: 0;
  border: 1px solid var(--stroke, rgba(255, 255, 255, .16)); border-radius: 10px;
  background: transparent; color: inherit; cursor: pointer;
}
.vst-ed-hear:hover, .vst-ed-more:hover, .vst-ed-more.on { background: var(--hover, rgba(255, 255, 255, .08)); }
.vst-ed-dash { opacity: .5; }
.vst-ed-list input {
  width: 100%; min-height: 40px; padding: 8px 12px;
  border: 1px solid var(--stroke, rgba(255, 255, 255, .16)); border-radius: 10px;
  background: color-mix(in srgb, var(--ink, #fff) 5%, var(--st-sheet, #14121f)); color: inherit;
  font-family: var(--vst-font, "Cairo Arabic", "MZL Geist", var(--font-ui, system-ui), sans-serif); font-size: 14.5px;
}
.vst-ed-list input.vst-ed-time {
  width: 84px; padding: 8px 6px; text-align: center;
  font-family: var(--font-ui, system-ui), sans-serif; font-size: 13px; font-variant-numeric: tabular-nums;
}
.vst-ed-list input:focus { outline: 2px solid var(--accent, #7c5cff); outline-offset: 0; border-color: transparent; }
.vst-ed-row.is-dirty .vst-ed-text { border-color: color-mix(in srgb, var(--accent, #7c5cff) 60%, var(--stroke, rgba(255, 255, 255, .16))); }
.vst-ed-row.is-new .vst-ed-text { border-style: dashed; border-color: var(--accent, #7c5cff); }
.vst-ed-list input[aria-invalid="true"] { border-color: #ff6b7a; }
.vst-ed-acts { display: flex; flex-wrap: wrap; gap: 6px; padding: 2px 0 4px; }
.vst-ed-acts button {
  display: inline-flex; align-items: center; gap: 6px; min-height: 36px; padding: 0 12px;
  border: 1px solid var(--stroke, rgba(255, 255, 255, .16)); border-radius: 999px;
  background: transparent; color: inherit; font: inherit; font-size: 12.5px; cursor: pointer;
}
.vst-ed-acts button:hover:not(:disabled) { background: var(--hover, rgba(255, 255, 255, .08)); }
.vst-ed-acts button:disabled { opacity: .4; cursor: default; }
.vst-ed-acts .vst-ed-del { color: #ff8a96; border-color: color-mix(in srgb, #ff6b7a 45%, transparent); }
.vst-ed-msg { margin: 0; font-size: 12px; opacity: .78; }
.vst-ed-msg.is-bad { opacity: 1; color: color-mix(in srgb, #ff6b7a 85%, var(--ink, #fff)); font-weight: 600; }
/* Phone: the words get the whole width on a line of their own, under the times and the buttons. */
@media (max-width: 560px) {
  .vst-ed-head { flex-wrap: wrap; }
  .vst-ed-row {
    grid-template-columns: 44px auto minmax(0, 1fr) 44px;
    grid-template-areas: "hear times . more" "text text text text" "acts acts acts acts" "msg msg msg msg";
    padding: 8px 6px; border-bottom: 1px solid var(--stroke, rgba(255, 255, 255, .16)); border-radius: 0;
  }
  .vst-ed-hear, .vst-ed-more { width: 44px; height: 44px; }
  .vst-ed-list input.vst-ed-time { width: 80px; min-height: 44px; }
  .vst-ed-acts button { min-height: 44px; }
}
.vst-ed-foot {
  display: flex; flex-wrap: wrap; align-items: center; gap: 8px;
  padding: 12px 18px; border-top: 1px solid var(--stroke, rgba(255, 255, 255, .16));
}
.vst-ed-grow { flex: 1 1 auto; }
.vst-ed-reset { border: 0; background: none; padding: 6px 0; color: var(--accent, #7c5cff); font: inherit; font-size: 13px; cursor: pointer; }
.vst-btn {
  min-height: 40px; padding: 0 18px;
  border: 1px solid var(--stroke); border-radius: 999px;
  background: transparent; color: inherit; font: inherit; font-size: 13.5px; font-weight: 600; cursor: pointer;
}
.vst-btn:hover:not(:disabled) { background: var(--hover); }
.vst-btn-primary { border-color: transparent; color: var(--accent-ink, #fff); background: var(--cta, linear-gradient(135deg, #4f7cff, #a35bff)); }
.vst-btn:disabled { opacity: .45; cursor: not-allowed; }

/* `.st-stage-full` is rendered through a portal to <body> (js/vocal-gpu/vocal-mixer.jsx), because
   `.tool` in the shared css/vocal-cosmic.css carries a backdrop-filter — and a filtered element
   becomes the containing block for its fixed descendants. Left in place the overlay was positioned
   against the card, not the window: measured on staging at 1500x1000 it painted at x=116, y=14 and
   ran 116px past the right edge, with the exit button and every control off-screen. The portal is
   the fix; the doubled selector below is the belt, so a future transform on any ancestor cannot
   quietly bring the bug back. */
.st-stage-full,
.st-stage.st-stage-full {
  position: fixed;
  inset: 0;
  /* above the site rail and the tweaks button: in a performance mode they clipped "NOW PLAYING"
     and sat on top of the template controls */
  z-index: 2147483000;
  /* the stage carries a fixed height by design; full screen must override it or the overlay covers
     only the top of the window and the page shows through underneath */
  height: 100dvh;
  max-height: 100dvh;
  /* NOT 100vw: that includes the scrollbar gutter and pushed the overlay wider than the window.
     `inset: 0` already gives the full width. */
  width: auto;
  border-radius: 0;
  border: 0;
  background: var(--bg, #0b0a14);
  display: flex;
  flex-direction: column;
}
.st-stage-full .vst { padding: clamp(10px, 2vw, 22px); }
.st-stage-full .vst-frame {
  --vst-fs-cur: calc(clamp(34px, 6.4vw, 88px) * var(--vst-scale));
  --vst-fs-side: calc(clamp(18px, 2.6vw, 38px) * var(--vst-scale));
}
.st-stage-full .vst-list .vst-line { font-size: calc(clamp(17px, 2vw, 26px) * var(--vst-scale)); }

/* ── no lyrics yet: the upsell lives where the words would be ────────────────────────────── */
.st-nolyrics {
  flex: 1 1 auto;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 10px;
  text-align: center;
  padding: clamp(16px, 3vw, 34px);
}

.st-nolyrics-art { display: flex; gap: 6px; align-items: flex-end; height: 34px; }
.st-nolyrics-art i {
  width: 7px;
  border-radius: 999px;
  background: var(--accent, #7c5cff);
  opacity: .5;
  animation: st-bounce 1.6s ease-in-out infinite;
}
.st-nolyrics-art i:nth-child(1) { height: 16px; animation-delay: 0s; }
.st-nolyrics-art i:nth-child(2) { height: 28px; animation-delay: .2s; }
.st-nolyrics-art i:nth-child(3) { height: 20px; animation-delay: .4s; }
@keyframes st-bounce { 0%, 100% { transform: scaleY(.6); } 50% { transform: scaleY(1); } }

/* Lyrics being made from the vocal stem (2026-09-25): the bars keep time faster and at full
   strength, so the stage visibly works rather than sitting there; the clock says for how long. */
.st-nolyrics.is-working .st-nolyrics-art i { opacity: 1; animation-duration: .7s; }
/* The clock stands where the button was, at the button's size, so the stage does not change height
   between the two states (measured on a phone: 316 -> 283px before). */
.st-gen-clock {
  margin-top: 6px; min-height: 44px; padding: 0 20px;
  display: inline-flex; align-items: center; justify-content: center;
  border: 1px solid color-mix(in srgb, var(--ink, #fff) 22%, transparent);
  border-radius: 999px;
  font-size: 14px; font-weight: 600; letter-spacing: .02em;
}
.st-nolyrics p.st-gen-err {
  opacity: 1; font-weight: 600;
  color: color-mix(in srgb, #ff6b7a 82%, var(--ink, #fff));
}
/* Standard has no lyrics (owner 2026-09-26): its empty stage offers the track again on Ultra, named on a
   pill that stands where the bars stand, so the fixed-height stage keeps the same rows. */
.st-ultra-pill {
  display: inline-flex; align-items: center; min-height: 24px; padding: 0 12px;
  border-radius: 999px;
  background: var(--cta, linear-gradient(135deg, #4f7cff, #a35bff));
  color: var(--accent-ink, #fff);
  font-size: 11px; font-weight: 700; letter-spacing: .14em; text-transform: uppercase;
}
.st-nolyrics h4 { margin: 4px 0 0; font-size: clamp(16px, 2vw, 20px); font-weight: 600; }
.st-nolyrics p { margin: 0; max-width: 46ch; font-size: 13.5px; opacity: .8; line-height: 1.5; text-wrap: pretty; }

.st-cta {
  margin-top: 6px;
  min-height: 44px;
  padding: 10px 20px;
  border: 0;
  border-radius: 999px;
  background: var(--cta, linear-gradient(135deg, #4f7cff, #a35bff));
  color: var(--accent-ink, #fff);
  font: inherit;
  font-weight: 600;
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  gap: 8px;
}

/* ── voice desk ──────────────────────────────────────────────────────────────────────────── */
.st-voice {
  display: flex;
  flex-direction: column;
  gap: 12px;
  padding-bottom: 14px;
  min-height: 0;
  overflow-y: auto;
  scrollbar-width: thin;
}

.st-rec-row {
  display: flex;
  align-items: center;
  gap: 14px;
  padding: 0 clamp(12px, 1.6vw, 18px);
}

.st-rec {
  flex: none;
  width: 84px; height: 84px;
  display: grid;
  place-items: center;
  gap: 2px;
  border: 2px solid color-mix(in srgb, #ff4d5e 55%, transparent);
  border-radius: 50%;
  background: color-mix(in srgb, #ff4d5e 14%, transparent);
  color: inherit;
  font: inherit;
  font-weight: 700;
  cursor: pointer;
}
.st-rec:disabled { opacity: .5; cursor: not-allowed; }
.st-rec-dot { width: 14px; height: 14px; border-radius: 50%; background: #ff4d5e; }
.st-rec-label { font-size: 12.5px; }
/* inside the 84 px circle the label stays within the circle's chord at its height in every language (2026-10-01:
   Romanian "Înregistrează" is 81 px and ran over the ring, Greek "Ηχογράφηση" touched it): a hair smaller, and a word
   too long for 68 px wraps — hyphenated in the page's language, anywhere as a last resort. The phone pill (below)
   has the room and lifts the limit. */
.st-rec .st-rec-label { font-size: 12px; max-width: 68px; line-height: 1.1; text-align: center; hyphens: auto; overflow-wrap: anywhere; }
.st-rec.on { background: color-mix(in srgb, #ff4d5e 26%, transparent); animation: st-pulse 1.4s ease-in-out infinite; }
@keyframes st-pulse { 0%, 100% { box-shadow: 0 0 0 0 color-mix(in srgb, #ff4d5e 45%, transparent); }
                      50% { box-shadow: 0 0 0 12px transparent; } }

.st-rec-info { flex: 1 1 auto; min-width: 0; }
.st-meter {
  height: 8px;
  border-radius: 999px;
  background: color-mix(in srgb, currentColor 14%, transparent);
  overflow: hidden;
  margin-bottom: 8px;
}
.st-meter span { display: block; height: 100%; width: 0; background: var(--accent, #7c5cff); }
.st-hint { margin: 0; font-size: 12.5px; opacity: .8; line-height: 1.45; }

.st-fx {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  padding: 0 clamp(12px, 1.6vw, 18px);
}
.st-fx-item { display: flex; align-items: center; gap: 6px; }

.st-chip {
  min-height: 36px;
  padding: 7px 14px;
  border: 1px solid var(--stroke, rgba(255, 255, 255, .18));
  border-radius: 999px;
  background: transparent;
  color: inherit;
  font: inherit;
  font-size: 12.5px;
  cursor: pointer;
}
.st-chip.on {
  border-color: var(--accent, #7c5cff);
  color: var(--accent, #7c5cff);
  background: color-mix(in srgb, var(--accent, #7c5cff) 12%, transparent);
}
.st-chip:disabled { opacity: .5; cursor: not-allowed; }
.st-fx-amt { width: 84px; height: 32px; }

.st-voice-opts {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 12px;
  padding: 0 clamp(12px, 1.6vw, 18px);
  font-size: 12.5px;
}
.st-opt { display: inline-flex; align-items: center; gap: 7px; min-height: 32px; cursor: pointer; }
.st-opt-hint { opacity: .7; }
.st-opt-num input { width: 64px; min-height: 32px; }
.st-link {
  border: 0;
  background: none;
  color: var(--accent, #7c5cff);
  font: inherit;
  font-size: 12.5px;
  min-height: 32px;
  padding: 0 2px;
  cursor: pointer;
  text-decoration: underline;
}
.st-link:disabled { opacity: .5; cursor: not-allowed; }
.st-cancel { margin-inline-start: 8px; }

/* ── the desk: one fixed box, two mixer styles inside it ─────────────────────────────────── */
/* Its height does not depend on which style is showing, so toggling moves NOTHING else on the
   page. When a job has more tracks than the box holds, the box scrolls — the page does not grow. */
.st-desk {
  height: var(--st-desk-h);
  padding: 10px;
  overflow: hidden;
  display: flex;
}
.st-rows {
  flex: 1 1 auto;
  /* a grid whose columns the rows share (subgrid, see .st-row) — not a column of separate grids */
  display: grid;
  grid-template-columns: 3px fit-content(132px) minmax(0, 1fr) auto auto auto auto auto;
  /* max-content, not auto: a row's min-height stands in for its content as the row's minimum, so an
     auto row was squeezed to fit the visible height (95px for 124px of tablet row) instead of the
     list scrolling — and each row's buttons ran over the next one. */
  grid-auto-rows: max-content;
  align-content: start;
  gap: 6px 12px;
  overflow-y: auto;
  overflow-x: hidden;
  scrollbar-width: thin;
  scrollbar-gutter: stable;
  /* without a colour, "thin" is the platform's bright default: a white bar down a dark desk */
  scrollbar-color: color-mix(in srgb, var(--ink, #fff) 26%, transparent) transparent;
}
.st-strips {
  flex: 1 1 auto;
  display: flex;
  gap: 10px;
  /* Centred, not stretched: a two-stem desk on a 1700px screen should read as two channel STRIPS,
     not two rooms with a fader lost in the middle of each. `safe` is the whole point of the
     keyword — plain `center` puts half of any overflow BEFORE the content box, where scrollLeft
     cannot reach it, and `allKeys` puts the singer's own take first, so on a seven-track job the
     "You" strip sat at x=-125 and could never be scrolled back into view. */
  justify-content: flex-start;
  justify-content: safe center;
  overflow-x: auto;
  /* The strips are ~332px tall and the desk is often shorter — with `hidden` the M/S/FX row was
     simply sliced off, with no way to reach it. */
  overflow-y: auto;
  scroll-snap-type: x proximity;
  scrollbar-width: thin;
  scrollbar-color: color-mix(in srgb, var(--ink, #fff) 26%, transparent) transparent;
}

.st-ch {
  /* A strip keeps strip proportions at every width; the desk centres them and scrolls when a
     six-stem job no longer fits. */
  flex: 0 0 auto;
  width: clamp(150px, 15vw, 200px);
  align-self: start;                 /* its own height, not stretched to a scroller that is shorter */
  scroll-snap-align: start;
  display: grid;
  gap: 8px;
  padding: 10px;
  border: 1px solid var(--stroke, rgba(255, 255, 255, .12));
  border-radius: 14px;
  background: color-mix(in srgb, var(--ch, var(--accent)) 7%, transparent);
}
.st-ch.is-off { opacity: .55; }

.st-ch-top { display: flex; align-items: center; justify-content: space-between; gap: 6px; }
.st-ch-name {
  font-size: 12.5px;
  font-weight: 600;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
/* (.st-ch-dl, the bare download arrow, was retired 2026-09-24 for .st-dlpill.) */

.st-ch-wave { display: flex; align-items: flex-end; gap: 1px; height: 26px; opacity: .55; }
.st-ch-wave i { flex: 1 1 auto; background: var(--ch, var(--accent)); border-radius: 1px; min-height: 2px; }

/* left meter | fader | right meter — the same axis the pan row labels underneath. */
.st-ch-body {
  display: grid;
  grid-template-columns: 7px minmax(0, 1fr) 7px;
  align-items: stretch;
  gap: 8px;
  height: 132px;
}

/* One bar per side. A single mono bar could not show a pan move at all - the ear heard the voice
   travel and the meter sat exactly where it was - and two bars huddled together at the left edge
   were no better, because both stood over the "L" of the pan row. */
.st-ch-body { --lvl-l: 0%; --lvl-r: 0%; }

.st-ch-lvl {
  position: relative;
  border-radius: 999px;
  background: color-mix(in srgb, currentColor 14%, transparent);
  overflow: hidden;
}
.st-ch-lvl::after {
  content: "";
  position: absolute;
  inset-inline: 0;
  bottom: 0;
  height: var(--lvl, 0%);
  border-radius: 999px;
  background: linear-gradient(to top, var(--ch, var(--accent)), color-mix(in srgb, var(--ch, var(--accent)) 40%, #fff));
}
.st-ch-lvl-l { --lvl: var(--lvl-l); }
.st-ch-lvl-r { --lvl: var(--lvl-r); }

/* The faders are drawn, not left to the browser: a native vertical range renders as a thin line
   with a round dot, which reads as a scrollbar rather than a fader you can grab. */
.st-ch-fader {
  flex: 1 1 auto;
  writing-mode: vertical-lr;
  direction: rtl;
  -webkit-appearance: none;
  appearance: none;
  width: 34px;
  height: 100%;
  margin: 0 auto;
  background: transparent;
  cursor: ns-resize;
}
.st-ch-fader::-webkit-slider-runnable-track {
  width: 8px;
  height: 100%;
  border-radius: 999px;
  background: color-mix(in srgb, currentColor 16%, transparent);
}
.st-ch-fader::-moz-range-track {
  width: 8px;
  border-radius: 999px;
  background: color-mix(in srgb, currentColor 16%, transparent);
}
.st-ch-fader::-webkit-slider-thumb {
  -webkit-appearance: none;
  width: 30px;
  height: 18px;
  margin-left: -11px;                  /* centre the cap on the 8px track */
  border-radius: 5px;
  border: 1px solid color-mix(in srgb, #000 22%, transparent);
  background: linear-gradient(180deg, color-mix(in srgb, var(--ch, var(--accent)) 82%, #fff), var(--ch, var(--accent)));
  box-shadow: 0 1px 3px rgba(0, 0, 0, .35);
}
.st-ch-fader::-moz-range-thumb {
  width: 30px;
  height: 18px;
  border-radius: 5px;
  border: 1px solid color-mix(in srgb, #000 22%, transparent);
  background: var(--ch, var(--accent));
}

.st-ch-pan-row { display: grid; gap: 2px; }
.st-ch-pan-lbl {
  display: flex;
  justify-content: space-between;
  font-size: 10.5px;
  letter-spacing: .04em;
  opacity: .7;
}
.st-ch-pan {
  width: 100%;
  height: 26px;
  -webkit-appearance: none;
  appearance: none;
  background: transparent;
  cursor: ew-resize;
}
.st-ch-pan::-webkit-slider-runnable-track {
  height: 4px;
  border-radius: 999px;
  /* the centre detent is drawn into the track, so "straight ahead" is visible at a glance */
  background:
    linear-gradient(currentColor, currentColor) center / 2px 10px no-repeat,
    color-mix(in srgb, currentColor 16%, transparent);
}
.st-ch-pan::-webkit-slider-thumb {
  -webkit-appearance: none;
  width: 14px; height: 14px;
  margin-top: -5px;
  border-radius: 50%;
  background: var(--ch, var(--accent));
  border: 1px solid color-mix(in srgb, #000 20%, transparent);
}
.st-ch-pan::-moz-range-thumb {
  width: 14px; height: 14px; border-radius: 50%;
  background: var(--ch, var(--accent));
  border: 1px solid color-mix(in srgb, #000 20%, transparent);
}

.st-ch-btns { display: flex; gap: 6px; }
.st-ms {
  flex: 1 1 auto;
  min-height: 34px;
  border: 1px solid color-mix(in srgb, currentColor 32%, transparent);
  border-radius: 9px;
  background: transparent;
  color: inherit;
  font: inherit;
  font-size: 12px;
  font-weight: 700;
  cursor: pointer;
}
.st-ms.on { border-color: var(--accent, #7c5cff); color: var(--accent, #7c5cff);
            background: color-mix(in srgb, var(--accent, #7c5cff) 14%, transparent); }

/* ── tablet: keep the full layout, restack rather than simplify ──────────────────────────── */
@media (max-width: 1100px) {
  .st-main { grid-template-columns: minmax(0, 1fr); }
  /* THE STAGE COLLAPSE. `.st-main` keeps `height: var(--st-main-h)` from the desktop rule, and the
     moment it stacks, that one height has to cover the stage AND the voice desk. Measured at
     834px: the karaoke screen came out 87px tall, all of it the old header — zero lyrics on
     screen. Stacked children size themselves. */
  .st-main { height: auto; }
  .st-stage { height: var(--st-stage-h); min-height: 0; }
  .st-voice { height: auto; }
  /* …and once .st-main is auto, the desk budget that subtracts --st-main-h from the viewport is
     measuring the wrong thing. Below 1100px the desk takes a plain share of the window instead —
     still ONE height for both mixer styles, so switching still shifts nothing. */
  .st { --st-desk-h: clamp(300px, 52vh, 620px); }
  .st-bar { grid-template-columns: minmax(0, 1fr) auto; }
  .st-transport { grid-column: 1 / -1; order: 3; }
  .st-bar-actions { justify-content: flex-end; }
  .st-ch { width: 150px; }
}

/* ── phone: stage first, horizontal faders, 44px targets ─────────────────────────────────── */
@media (max-width: 760px) {
  .st { --st-radius: 14px; }
  .st-bar { grid-template-columns: minmax(0, 1fr); padding: 12px; }
  .st-bar-id { order: 1; }
  .st-transport { order: 2; grid-column: auto; }
  .st-bar-actions { order: 3; justify-content: stretch; }
  .st-bar-actions > * { flex: 1 1 auto; min-height: 44px; justify-content: center; }
  .st-play { width: 52px; height: 52px; }

  /* On a phone the toolbar is three rows (views 2 x 2, then the actions), so a FIXED stage height
     left the lyric screen whatever was over: measured 50px on an iPhone 13, three lines overlapping.
     The stage grows with its toolbar instead, and the SCREEN has the height. */
  .st-stage { min-height: 0; height: auto; }
  .st-stage:not(.st-stage-full) .vst-screen { flex: none; height: clamp(190px, 30vh, 270px); }
  /* stacked, so the single-row height budget no longer applies to the pair of them */
  .st-main { height: auto; }
  .st-voice { height: auto; overflow: visible; }
  /* A phone strip is a full-width ROW — which is what the .st-ch rules just below have always
     assumed, while the flex container stayed horizontal underneath them: seven strips then shrank
     to 31px each and their download buttons and M/S/FX rows painted on top of the neighbours. */
  .st-strips { flex-direction: column; overflow-x: hidden; }
  .st-ch { width: auto; align-self: stretch; }
  .st-rec-row { flex-direction: column; align-items: stretch; text-align: center; gap: 10px; }
  /* the dot and the word read as one control, centred — not a dot stranded at the far edge */
  .st-rec {
    width: 100%; height: 60px; border-radius: 16px;
    display: flex; flex-direction: row; align-items: center; justify-content: center; gap: 10px;
  }
  .st-rec .st-rec-label { font-size: 12.5px; max-width: none; }
  .st-chip { min-height: 44px; padding: 10px 16px; }
  /* Only a chip that carries a slider needs the full row; the plain ones would otherwise stack
     one per line and push the faders off the screen. */
  .st-fx-item:has(.st-fx-amt) { flex: 1 1 100%; }
  .st-fx-item:has(.st-fx-amt) .st-fx-amt { flex: 1 1 auto; width: auto; }

  /* Two full-width GRIDS of equal cells (owner 2026-09-24): the views on top, the actions below,
     every button the same width as the one above it and both rows flush with both edges. What was
     here before — a sideways-scrolling strip of views and a right-aligned second row — clipped
     "Spotlight" and left the second row hanging off the right for no reason. */
  .vst-tools { display: grid; grid-template-columns: minmax(0, 1fr); gap: 8px; }
  .vst-tpl { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 6px; }
  .vst-tpl button { min-height: 44px; padding: 8px 4px; font-size: 12.5px; line-height: 1.2; }
  /* one equal column per action, however many there are (Edit is absent without lyrics to edit) */
  .vst-acts { display: grid; grid-auto-flow: column; grid-auto-columns: minmax(0, 1fr); gap: 6px; }
  /* the icon sits above its label here (2026-10-01): beside it, a word such as "Bearbeiten", "Изменить" or
     "Szerkesztés" left the label ~60 px and broke mid-word; above it, the label has the button's whole width */
  .vst-act { min-height: 44px; padding: 6px 4px; flex-direction: column; gap: 3px; line-height: 1.15; }
  .vst-pop { inset-inline: 0; width: auto; }

  /* A vertical fader is unusable with a thumb: the strip becomes a row and the fader lies flat. */
  .st-desk { flex-direction: column; overflow-x: visible; }
  .st-ch { flex: 1 1 auto; max-width: none; min-width: 0;
           grid-template-columns: 1fr auto; align-items: center; }
  .st-ch-top { grid-column: 1 / -1; }
  .st-ch-wave { grid-column: 1 / -1; height: 20px; }
  /* Stacked, not side by side: sharing the row left the fader half a screen wide and made the
     level meter read as a second slider. */
  .st-ch-body { grid-column: 1 / -1; height: auto; flex-direction: column; align-items: stretch; gap: 6px; }
  /* Flat on a phone: the bars stack above the fader and each carries its own letter, since there
     is no left-right axis left to stand on. */
  .st-ch-body {
    grid-template-columns: 12px minmax(0, 1fr);
    grid-template-rows: auto auto auto;
    gap: 5px 6px;
    height: auto;
  }
  .st-ch-lvl { grid-column: 2; height: 6px; }
  .st-ch-lvl-l { grid-row: 1; }
  .st-ch-lvl-r { grid-row: 2; }
  .st-ch-fader { grid-column: 1 / -1; grid-row: 3; }
  .st-ch-lvl::before {
    content: attr(data-side);
    position: absolute;
    inset-inline-end: 100%;
    margin-inline-end: 5px;
    top: 50%;
    transform: translateY(-50%);
    font-size: 9px;
    font-weight: 700;
    opacity: .6;
  }
  .st-ch-lvl::after {
    inset-inline: 0 auto;
    inset-block: 0;
    width: var(--lvl, 0%);
    height: 100%;
    background: linear-gradient(to right, var(--ch, var(--accent)), color-mix(in srgb, var(--ch, var(--accent)) 40%, #fff));
  }
  .st-ch-fader {
    writing-mode: horizontal-tb;
    direction: ltr;
    width: 100%;
    height: 44px;
    cursor: pointer;
  }
  .st-ch-fader::-webkit-slider-runnable-track { width: 100%; height: 8px; }
  .st-ch-fader::-moz-range-track { height: 8px; }
  /* the vertical cap's offset is meaningless once the fader lies flat */
  .st-ch-fader::-webkit-slider-thumb { width: 22px; height: 26px; margin-left: 0; margin-top: -9px; }
  .st-ch-fader::-moz-range-thumb { width: 22px; height: 26px; }
  /* Both of these are direct children of the strip's own two-column grid, and neither was spanned
     — so the dB readout took column 1 and the WHOLE pan row (label + 44px slider) was squeezed into
     the narrow auto column beside it. */
  .st-ch-db { grid-column: 1 / -1; }
  .st-ch-pan-row { grid-column: 1 / -1; }
  .st-ch-pan { grid-column: 1 / -1; height: 44px; }
  .st-ch-btns { grid-column: 1 / -1; }
  .st-ch > .st-dlpill { grid-column: 1 / -1; min-height: 44px; }
  .st-ms { min-height: 44px; }
}

/* Reduce Motion: the lyric sweep stays — it carries the timing, which is the content — but the
   decorative pulsing and bouncing stop. */
@media (prefers-reduced-motion: reduce) {
  .st-rec.on, .st-nolyrics-art i, .vst-aurora i, .vst-stars i, .vst-beams i { animation: none; }
  .vst-frame-bar .vst-slot > * { animation: none; }
  .vst-rail > .vst-line { transition: opacity 160ms linear, --vst-dim-k 160ms linear !important; }
  .vst-list .vst-line { transition: --vst-dim-k 160ms linear !important; }
}

/* ═══════════════════════════════════════════════════════════════════════════════════════════════
   2026-09-23 — horizontal track rows, rotary knobs, the per-track effect rack, and the rebuilt
   voice desk. Appended after the existing media queries so the responsive rules at the bottom of
   this block are the last word.
   ═════════════════════════════════════════════════════════════════════════════════════════════ */

/* ── a track as a horizontal lane ──────────────────────────────────────────────────────────── */
/* Named areas, so each width re-places the SAME children instead of re-ordering markup. On a desktop
   the download is its own column at the far end — the waveform gave up about a quarter of its
   width for it and for larger controls (owner 2026-09-24). */
.st-row {
  display: grid;
  /* The name column was 92-118px for names that are six letters long, which left a gap the owner
     marked between "Vocals" and its waveform; that width now goes to the L/R meters (2026-09-24).
     fit-content: as wide as the longest name on the desk, never more than 132px (then it ellipsizes).
     132, not 96: a voice track's name has its remove button beside it, and "My voice 2" + x came to
     ~104px, so it showed as "My voice..." (2026-09-28). A desk of stems alone is exactly as wide as before. */
  grid-template-columns: 3px fit-content(132px) minmax(0, 1fr) auto auto auto auto auto;
  /* ...but fit-content in a row's OWN grid is that row's longest name, so "Vocals" pushed its waveform
     further right than "Other" did, and a seek showed the two lanes out of line (owner 2026-09-25).
     The rows now take their columns from the list (.st-rows below): one set of tracks for every
     row, so every waveform starts AND ends at the same x — and the playheads agree. */
  grid-template-columns: subgrid;
  grid-column: 1 / -1;
  grid-template-areas: "sp id wave meter vol pan btns dl";
  align-items: center;
  gap: 12px;
  min-height: var(--st-row-h);
  padding: 6px 12px 6px 0;
  border: 1px solid var(--stroke);
  border-radius: 12px;
  background: color-mix(in srgb, var(--ch, var(--accent)) 7%, transparent);
  flex: 0 0 auto;
}
.st-row.is-off { opacity: .5; }
.st-row-spine {
  align-self: stretch;
  border-radius: 12px 0 0 12px;
  background: var(--ch, var(--accent));
}
.st-row-spine { grid-area: sp; }
.st-row-id { grid-area: id; min-width: 0; }
.st-row-wave { grid-area: wave; }
.st-row-meter { grid-area: meter; }
.st-row > .st-knob { grid-area: vol; }
.st-row > .st-knob-pan { grid-area: pan; }
.st-row-btns { grid-area: btns; }
.st-row > .st-dlpill { grid-area: dl; }
.st-row-name {
  font-size: 12.5px;
  font-weight: 600;
  line-height: 1.2;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* The waveform IS the navigation: it shows where a take starts, and a click seeks there. */
.st-row-wave {
  position: relative;
  display: flex;
  align-items: center;
  gap: 1px;
  height: 40px;
  padding: 0;
  border: 0;
  border-radius: 6px;
  background: transparent;
  cursor: pointer;
  overflow: hidden;
}
.st-row-wave-bars {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  gap: 1px;
}
.st-row-wave-bars i {
  flex: 1 1 auto;
  min-width: 1px;
  border-radius: 1px;
  background: color-mix(in srgb, var(--ch, var(--accent)) 42%, transparent);
}
/* What has NOT been played yet is veiled with the page's own background — ONE element, not a class
   on each of 168 bars, which would be about 1,200 elements of React reconciliation per animation
   frame across seven tracks. Veiling the unplayed side rather than brightening the played side
   matters: brightening washed the played part out on the light theme, so it read as the faded one. */
/* Both move by TRANSFORM, written from the studio clock one device pixel at a time (2026-09-29): they used
   to take a `left` from React state on every animation frame, which re-rendered the whole studio. */
.st-row-unplayed {
  position: absolute;
  top: 0; bottom: 0; left: 0; right: 0;
  transform-origin: 100% 50%;                     /* scaleX(1 - played), from the right edge */
  background: color-mix(in srgb, var(--bg, #0b0a1a) 40%, transparent);   /* 66% until 2026-10-01: with the detailed canvas wave it washed the whole lane out */
  pointer-events: none;
}
.st-row-head {                                    /* the full lane, translated by the played fraction */
  position: absolute;
  top: 0; bottom: 0; left: 0; right: 0;
  pointer-events: none;
}
.st-row-head::before {
  content: "";
  position: absolute;
  top: 0; bottom: 0; left: -1px;
  width: 2px;
  background: var(--ink, #fff);
  box-shadow: 0 0 6px 1px color-mix(in srgb, var(--ink, #fff) 55%, transparent);
}

/* Stereo meter: two thin columns, L then R, so a pan move is visible as well as audible. */
.st-row-meter {
  --lvl-l: 0%;
  --lvl-r: 0%;
  display: grid;
  grid-template-columns: 12px 12px;
  gap: 4px;
  align-items: end;
  height: 46px;
}
.st-row-lvl {
  position: relative;
  height: 100%;
  border-radius: 3px;
  background: color-mix(in srgb, var(--ink, #fff) 12%, transparent);
  overflow: hidden;
}
.st-row-lvl::after {
  content: "";
  position: absolute;
  left: 0; right: 0; bottom: 0;
  height: var(--lvl, 0%);
  border-radius: 3px;
  background: linear-gradient(to top, var(--ch, var(--accent)), color-mix(in srgb, var(--ch, var(--accent)) 40%, #fff));
  transition: height .06s linear;
}
.st-row-lvl-l { --lvl: var(--lvl-l); }
.st-row-lvl-r { --lvl: var(--lvl-r); }

.st-row-btns { display: flex; gap: 6px; }
.st-row-btns .st-ms { min-width: 40px; min-height: 38px; font-size: 12.5px; }

/* ── the download: a button that looks like one ───────────────────────────────────────────────
   It was a bare grey arrow beside the name, and nothing about it said "this track can be taken
   away". Now: the glyph AND the word, in the accent, with a real target — and an FX tag when the
   download carries the track's effects, so no one is surprised by what lands on their disk. */
.st-dlpill {
  display: inline-flex; align-items: center; justify-content: center; gap: 7px;
  min-height: 38px; min-width: 138px; padding: 0 16px;
  border: 1.5px solid color-mix(in srgb, var(--accent, #7c5cff) 72%, transparent);
  border-radius: 999px;
  background: color-mix(in srgb, var(--accent, #7c5cff) 16%, transparent);
  color: var(--ink, #fff);
  font: inherit; font-size: 13px; font-weight: 700; white-space: nowrap;
  cursor: pointer;
  transition: background .15s, color .15s, border-color .15s;
}
.st-dlpill svg { width: 17px; height: 17px; flex: none; color: var(--accent, #7c5cff); }
.st-dlpill:hover {
  background: var(--accent, #7c5cff); border-color: transparent; color: var(--accent-ink, #fff);
}
.st-dlpill:hover svg { color: inherit; }
.st-dlpill:focus-visible { outline: 2px solid var(--accent, #7c5cff); outline-offset: 2px; }
.st-dlpill-fx {
  font-style: normal; font-size: 10px; font-weight: 800; letter-spacing: .04em;
  padding: 2px 6px; border-radius: 6px;
  background: var(--accent, #7c5cff); color: var(--accent-ink, #fff);
}
.st-dlpill:hover .st-dlpill-fx { background: color-mix(in srgb, #fff 24%, transparent); }
/* a strip closes with it, full width */
.st-ch > .st-dlpill { width: 100%; min-width: 0; }

/* ── the rotary knob ───────────────────────────────────────────────────────────────────────── */
.st-knob { display: grid; justify-items: center; gap: 0; line-height: 1; }
.st-knob-dial {
  cursor: ns-resize;
  touch-action: none;                /* or a vertical drag scrolls the desk instead of turning it */
  border-radius: 50%;
  display: grid;
  place-items: center;
}
.st-knob-dial:focus-visible { outline: 2px solid var(--accent, #7c5cff); outline-offset: 2px; }
.st-knob-track { fill: none; stroke: color-mix(in srgb, var(--ink, #fff) 16%, transparent); stroke-width: 3; stroke-linecap: round; }
.st-knob-fill { fill: none; stroke: var(--accent, #7c5cff); stroke-width: 3; stroke-linecap: round; }
.st-knob-ptr { stroke: var(--ink, #fff); stroke-width: 2; stroke-linecap: round; opacity: .9; }
.st-knob-pan .st-knob-fill { stroke: var(--stem-3, #46c8b6); }
.st-knob-val { font-size: 10.5px; font-variant-numeric: tabular-nums; opacity: .92; margin-top: 2px; }
.st-knob-lbl { font-size: 9.5px; letter-spacing: .04em; text-transform: uppercase; opacity: .6; }
.st-knob.is-set .st-knob-val { color: var(--accent, #7c5cff); font-weight: 600; }

/* ── the strips gain a dB readout and an FX button ─────────────────────────────────────────── */
.st-ch-db { font-size: 10.5px; text-align: center; opacity: .75; font-variant-numeric: tabular-nums; }
.st-fxbtn { position: relative; font-size: 10.5px; letter-spacing: .02em; }
.st-fxdot {
  position: absolute;
  top: 3px; inset-inline-end: 3px;
  width: 5px; height: 5px;
  border-radius: 50%;
  background: var(--accent, #7c5cff);
}
.st-fxbtn.on { border-color: var(--accent, #7c5cff); color: var(--accent, #7c5cff); }

/* ── voice desk: upload button, mixer-style toggle ─────────────────────────────────────────── */
.st-upload {
  margin: 0 clamp(12px, 1.6vw, 18px);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  min-height: 40px;
  padding: 0 16px;
  border: 1px solid var(--stroke);
  border-radius: 12px;
  background: transparent;
  color: inherit;
  font: inherit;
  font-size: 13px;
  cursor: pointer;
}
.st-upload svg { width: 16px; height: 16px; flex: none; }
.st-upload:hover:not(:disabled) { background: var(--hover); }
.st-upload:disabled { opacity: .5; cursor: not-allowed; }
.st-upload:focus-visible { outline: 2px solid var(--accent, #7c5cff); outline-offset: 2px; }

/* Every row of the voice desk is inset by the same gutter (.st-voice-head, .st-rec-row,
   .st-voice-opts). These two were added later and missed it, so on a phone the MIXER STYLE label
   sat flush against the panel border and read as clipped. */
.st-deskstyle { display: flex; align-items: center; justify-content: space-between; gap: 10px; flex-wrap: wrap;
                padding: 0 clamp(12px, 1.6vw, 18px); }
.st-deskstyle-lbl { font-size: 11.5px; letter-spacing: .04em; text-transform: uppercase; opacity: .65; }
.st-seg { display: inline-flex; border: 1px solid var(--stroke); border-radius: 10px; overflow: hidden; }
.st-seg button {
  display: inline-flex; align-items: center; gap: 6px;
  min-height: 34px; padding: 0 12px;
  border: 0; background: transparent; color: inherit; font: inherit; font-size: 12.5px; cursor: pointer;
}
.st-seg button + button { border-inline-start: 1px solid var(--stroke); }
.st-seg button.on { background: var(--accent, #7c5cff); color: var(--accent-ink, #fff); }
.st-seg button:focus-visible { outline: 2px solid var(--accent, #7c5cff); outline-offset: -2px; }

/* ── the download scope menu ───────────────────────────────────────────────────────────────── */
.st-dl { position: relative; display: inline-flex; }
/* A download at work keeps the width the button had (owner 2026-09-30: the progress text "doesn't have a fixed width, so
   it causes the whole bar components to shift"): the words are cut to fit, the percentage keeps a fixed-width box of
   its own, and a thin fill along the bottom shows how far it is. */
.st-dl-lock { overflow: hidden; position: relative; }
.st-dl-lock .st-dl-lbl { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.st-dl-pct { flex: none; min-width: 3.4ch; text-align: end; font-variant-numeric: tabular-nums; }
.st-dl-fill {
  position: absolute; left: 0; right: 0; bottom: 0; height: 3px; pointer-events: none;
  background: var(--cta, linear-gradient(135deg, #4f7cff, #a35bff));
  transform-origin: left center; transform: scaleX(var(--dl-pct, 0)); transition: transform .4s ease;
}
[dir="rtl"] .st-dl-fill { transform-origin: right center; }
.st-dl-chev {
  width: 0; height: 0; margin-inline-start: 6px;
  border-left: 4px solid transparent; border-right: 4px solid transparent;
  border-top: 5px solid currentColor; opacity: .8;
}
.st-dl-menu {
  position: absolute;
  z-index: 40;
  top: calc(100% + 6px);
  inset-inline-end: 0;
  min-width: 290px;
  padding: 6px;
  display: grid;
  gap: 2px;
  border: 1px solid var(--stroke);
  border-radius: 12px;
  background: var(--st-sheet);
  color: var(--ink);
  box-shadow: 0 18px 48px -20px rgba(0, 0, 0, .55);
}
.st-dl-menu button {
  display: grid; gap: 1px; text-align: start;
  padding: 9px 11px; min-height: 44px;
  border: 0; border-radius: 9px; background: transparent; color: inherit; font: inherit; cursor: pointer;
}
.st-dl-menu button:hover { background: var(--hover); }
.st-dl-menu button:focus-visible { outline: 2px solid var(--accent, #7c5cff); outline-offset: -2px; }
.st-dl-menu b { font-size: 13px; font-weight: 600; }
.st-dl-menu em { font-size: 11.5px; font-style: normal; opacity: .72; }

/* ── the per-track effect rack ─────────────────────────────────────────────────────────────── */
.st-rack-wrap {
  position: fixed;
  inset: 0;
  z-index: 2147483000;               /* the cosmic rail sits at 6000 and would otherwise cover it */
  display: grid;
  place-items: center;
  padding: 16px;
  background: rgba(4, 3, 14, .55);
  backdrop-filter: blur(3px);
}
.st-rack {
  width: min(560px, 100%);
  max-height: min(86vh, 760px);
  display: flex;
  flex-direction: column;
  border: 1px solid var(--stroke);
  border-radius: 18px;
  background: var(--st-sheet);
  color: var(--ink);
  box-shadow: 0 30px 80px -30px rgba(0, 0, 0, .8);
  overflow: hidden;
}
.st-rack-head {
  display: flex; align-items: center; justify-content: space-between; gap: 10px;
  padding: 14px 16px;
  border-bottom: 1px solid var(--stroke);
}
.st-rack-head h4 { margin: 0; font-size: 15px; font-weight: 600; }
.st-rack-track { opacity: .65; font-weight: 400; }
.st-rack-head-r { display: flex; align-items: center; gap: 8px; }
.st-rack-reset {
  border: 1px solid var(--stroke); border-radius: 9px; background: transparent; color: inherit;
  font: inherit; font-size: 12px; min-height: 32px; padding: 0 10px; cursor: pointer;
}
.st-rack-reset:hover { background: var(--hover); }

.st-rack-presets {
  display: flex; flex-wrap: wrap; gap: 6px;
  padding: 10px 16px;
  border-bottom: 1px solid var(--stroke);
}
.st-rack-preset {
  min-height: 32px; padding: 0 12px;
  border: 1px solid var(--stroke); border-radius: 999px;
  background: transparent; color: inherit; font: inherit; font-size: 12.5px; cursor: pointer;
}
.st-rack-preset:hover { background: var(--hover); }
.st-rack-preset:focus-visible { outline: 2px solid var(--accent, #7c5cff); outline-offset: 2px; }

.st-rack-body {
  overflow-y: auto;
  padding: 8px;
  display: flex;
  flex-direction: column;
  gap: 6px;
  scrollbar-width: thin;
}

.st-mod { flex: 0 0 auto; border: 1px solid var(--stroke); border-radius: 12px; overflow: hidden; }
.st-mod.is-on { border-color: color-mix(in srgb, var(--accent, #7c5cff) 55%, var(--stroke)); }
.st-mod-head {
  width: 100%;
  display: grid; grid-template-columns: 10px minmax(0, 1fr) auto 14px;
  align-items: center; gap: 9px;
  min-height: 44px; padding: 0 12px;
  border: 0; background: transparent; color: inherit; font: inherit; text-align: start; cursor: pointer;
}
.st-mod-head:hover { background: var(--hover); }
.st-mod-head:focus-visible { outline: 2px solid var(--accent, #7c5cff); outline-offset: -2px; }
.st-mod-dot {
  width: 8px; height: 8px; border-radius: 50%;
  background: color-mix(in srgb, var(--ink, #fff) 22%, transparent);
}
.st-mod.is-on .st-mod-dot { background: var(--accent, #7c5cff); box-shadow: 0 0 0 3px color-mix(in srgb, var(--accent, #7c5cff) 22%, transparent); }
.st-mod-name { font-size: 13.5px; font-weight: 600; }
/* never colour alone — the state is a word as well as a dot */
.st-mod-state { font-size: 11px; letter-spacing: .06em; text-transform: uppercase; opacity: .6; }
.st-mod.is-on .st-mod-state { color: var(--accent, #7c5cff); opacity: 1; }
.st-mod-chev {
  width: 0; height: 0; justify-self: end;
  border-left: 4px solid transparent; border-right: 4px solid transparent;
  border-top: 5px solid currentColor; opacity: .55;
  transition: transform .15s ease;
}
.st-mod.is-open .st-mod-chev { transform: rotate(180deg); }
.st-mod-body { padding: 2px 12px 12px; }
.st-mod-hint { margin: 0 0 10px; font-size: 12px; opacity: .7; }
.st-mod-note { margin: 10px 0 0; font-size: 11.5px; opacity: .62; font-style: italic; }
.st-mod-knobs { display: flex; flex-wrap: wrap; align-items: flex-start; gap: 14px; }
.st-mod-off {
  align-self: center;
  min-height: 32px; padding: 0 12px;
  border: 1px solid var(--stroke); border-radius: 9px;
  background: transparent; color: inherit; font: inherit; font-size: 12px; cursor: pointer;
}
.st-mod-off:hover { background: var(--hover); }

/* ── responsive ────────────────────────────────────────────────────────────────────────────── */
/* Tablet: the row keeps every control, only tighter. Deferring the compact view for as long as
   possible is the rule this sheet opens with. */
@media (max-width: 1100px) {
  /* One line no longer holds a waveform worth the name next to a 138px download and the larger
     controls (at 768px it would be ~130px), so the name and the download take a line of their own. */
  .st-rows { grid-template-columns: 3px minmax(0, 1fr) auto auto auto auto; }
  .st-row {
    grid-template-columns: 3px minmax(0, 1fr) auto auto auto auto;
    grid-template-columns: subgrid;
    grid-template-areas:
      "sp id   id    id  id  dl"
      "sp wave meter vol pan btns";
    row-gap: 6px; column-gap: 12px;
    padding: 8px 12px 8px 0;
  }
  .st-row-wave { height: 40px; }
  .st-row > .st-dlpill { justify-self: end; min-height: 34px; min-width: 124px; }
}

/* Phone: the waveform takes its own line above the controls, because a 40px-wide waveform is not a
   waveform. The row grows to two lines rather than dropping anything. */
@media (max-width: 760px) {
  /* 148 was the sum of the three grid tracks (30 + 52 + 66) with the 12px of row-gap, the 18px of
     padding and the 2px of border all forgotten — 32px short, which is exactly how far the knob
     labels spilled into the next track. The row is auto-height now, so this is only the rhythm. */
  .st { --st-row-h: 240px; --st-stage-h: clamp(210px, 34vh, 300px); }
  /* Three lines: identity, then the waveform across the WHOLE row, then the controls. A waveform
     sharing a 1fr column with two knobs and three buttons came out about 30px wide, which is not a
     waveform — and being able to see where a take starts is the point of this layout. */
  /* A phone row WRAPS instead of sharing grid columns (2026-09-24). A shared grid had to fit the
     meter, two knobs and a labelled download into columns fixed by the other lines, and it did not:
     measured, the download sat 13px over the PAN knob, its label was clipped at ~100px on an iPhone,
     and at 360px M / S / FX ran over the track name. Now each line takes what it needs:
       1  the name (grows) and M / S / FX (44px each)
       2  the waveform, full width
       3  the meter, VOL and PAN
       4  the download, full width — ALWAYS its own line: on the knobs' line it fitted a Pixel 5
          and wrapped on an iPhone 13, three pixels apart, so two phones got two layouts. */
  /* every phone waveform is the row's full width, so a plain column already lines them up */
  .st-rows { display: flex; flex-direction: column; }
  .st-row {
    display: flex; flex-wrap: wrap; align-items: center;
    gap: 8px 6px;
    position: relative;
    padding: 10px 12px 12px 14px;
    min-height: var(--st-row-h);
    container-type: inline-size;
  }
  .st-row-spine { position: absolute; left: 0; top: 0; bottom: 0; width: 3px; }
  .st-row-id    { order: 1; flex: 1 1 60px; min-width: 0; }
  /* A track you may rename (a take, a clone, the You lane) gets the whole first line for its name, and M/S/FX
     step down to the right edge, where every stem row has them. Beside the x and M/S/FX a 390 px phone left the
     name 58 px — "Guest B" read "Gues..." — and the rename field 66 px, where "Tap test" showed as "ap test"
     (2026-09-28). One layout for both, so opening the field moves nothing. */
  .st-row:has(.st-name-btn, .st-name-edit) .st-row-id { flex-basis: 100%; }
  .st-row:has(.st-name-btn, .st-name-edit) .st-row-btns { margin-inline-start: auto; }
  .st-row-id .st-name-edit { flex: 1 1 auto; width: auto; max-width: 18ch; }
  .st-row-btns  { order: 2; flex: none; display: grid; grid-template-columns: repeat(3, 44px); gap: 6px; }
  .st-row-btns .st-ms { min-width: 0; min-height: 44px; }
  .st-row-wave  { order: 3; flex: 1 1 100%; height: 52px; }
  .st-row-meter { order: 4; flex: none; height: 50px; grid-template-columns: 10px 10px; gap: 4px; }
  .st-row > .st-knob { order: 5; flex: none; }
  .st-row > .st-dlpill {
    order: 6; flex: 1 1 100%; min-width: 0; min-height: 44px;
    padding: 0 12px; gap: 7px; font-size: 13px;
  }
  .st-rack { max-height: 92vh; width: 100%; }
  .st-mod-knobs { gap: 10px; }
  .st-dl-menu { min-width: min(300px, 86vw); }
  .st-deskstyle { justify-content: flex-start; }
}

/* A knob is a pointer-precision control; on a coarse pointer it gets a real target. */
@media (pointer: coarse) {
  .st-knob-dial { min-width: 44px; min-height: 44px; }
  .st-seg button { min-height: 44px; }
  .st-rack-preset { min-height: 40px; }
}

@media (prefers-reduced-motion: reduce) {
  .st-row-lvl::after { transition: none; }
  .st-mod-chev { transition: none; }
}

/* A saved project saves itself — there is no Save button — so the sub-line says so, and says so
   plainly when a save did not land (it retries on the next change and as the page goes away). */
.st-savestate { white-space: nowrap; }
.st-savestate.is-err { color: var(--danger, #e5484d); font-weight: 600; }

/* A compact Style panel on a phone (owner 2026-09-26: "make the style panel smaller on mobile"). At
   full width it still laid out like the desktop one — ten font tiles in five rows, colours in two,
   backgrounds in three, Size and Glow on rows of their own — and scrolled inside itself. Here every
   choice is ONE row: fonts and backgrounds scroll sideways (the tile cut off at the edge says there
   is more), the colours share a row, and Size and Glow sit side by side. */
@media (max-width: 760px) {
  .vst-pop-style { padding: 12px; gap: 10px; }
  .vst-pop-style .vst-pop-sec { gap: 6px; }
  .vst-pop-style .vst-pop-row { column-gap: 8px; }
  /* Size + Glow: a wrapping row, so the pair never collides on a narrow panel (a fixed two-column grid
     ran "A+" into "GLOW" at 390 px, where the panel is only ~266 px wide) */
  .vst-pop-style .vst-pop-pair {
    display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: 8px 16px;
  }
  .vst-pop-style .vst-pop-pair > .vst-pop-row { flex: 0 0 auto; }
  /* the strips scroll edge to edge of the panel, their first tile still aligned with the labels; the
     vertical padding keeps a selected tile's outline and a swatch's ring inside the scroller */
  .vst-pop-style .vst-fonts,
  .vst-pop-style .vst-bgs,
  .vst-pop-style .vst-colors {
    display: flex; flex-wrap: nowrap; gap: 6px;
    overflow-x: auto; overscroll-behavior-x: contain; scroll-snap-type: x proximity;
    scrollbar-width: none;
    margin-inline: -12px; padding: 4px 12px; scroll-padding-inline: 12px;
  }
  .vst-pop-style .vst-fonts::-webkit-scrollbar,
  .vst-pop-style .vst-bgs::-webkit-scrollbar,
  .vst-pop-style .vst-colors::-webkit-scrollbar { display: none; }
  .vst-pop-style .vst-font { flex: 0 0 76px; min-height: 48px; padding: 5px 4px; scroll-snap-align: start; }
  .vst-pop-style .vst-font b { font-size: 17px; }
  .vst-pop-style .vst-bgt { flex: 0 0 62px; padding: 4px; gap: 3px; scroll-snap-align: start; }
  .vst-pop-style .vst-sw { height: 28px; font-size: 12.5px; }
  .vst-pop-style .vst-color { flex: 0 0 auto; }
}

/* Very narrow phones: "Karaoke bar" would not fit a quarter of the row, so the views go 2 x 2. */
@media (max-width: 420px) {
  .vst-tpl { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}
@media (pointer: coarse) {
  .vst-act, .vst-tpl button { min-height: 44px; }
  .vst-color { width: 38px; height: 38px; }
}

/* Very narrow rows (a 320px phone) tighten M / S / FX before they would run over the name. */
@container (max-width: 210px) {
  .st-row-btns { grid-template-columns: repeat(3, 38px); gap: 4px; }
}

/* ── "My voice" (js/vocal-gpu/vocal-voiceclone.jsx, 2026-09-28) ──────────────────────────────────
   The way in is where a DAW adds a track: the end of the list, as a dashed row that reads as "+",
   never as one more stem. The sheet is PORTALLED to <body> (its tokens come from the portal rule at
   the top of this file), sized like the effect rack. */
.st-row-addwrap { grid-column: 1 / -1; }
.st-addtrack {
  width: 100%;
  min-height: var(--st-row-h, 80px);
  display: flex;
  align-items: center;
  gap: 14px;
  padding: 10px 16px;
  border: 1.5px dashed color-mix(in srgb, var(--accent2, var(--accent, #7c5cff)) 55%, var(--stroke));
  border-radius: 12px;
  background: color-mix(in srgb, var(--accent2, var(--accent, #7c5cff)) 6%, transparent);
  color: inherit;
  font: inherit;
  text-align: start;
  cursor: pointer;
}
.st-addtrack:hover { background: color-mix(in srgb, var(--accent2, var(--accent, #7c5cff)) 12%, transparent); }
.st-addtrack:focus-visible { outline: 2px solid var(--accent, #7c5cff); outline-offset: 2px; }
.st-addtrack-plus {
  flex: none;
  width: 40px; height: 40px;
  border-radius: 50%;
  display: grid;
  place-items: center;
  font-size: 22px;
  line-height: 1;
  background: color-mix(in srgb, var(--accent2, var(--accent, #7c5cff)) 18%, transparent);
  color: var(--ink);
}
.st-addtrack-txt { display: flex; flex-direction: column; gap: 3px; min-width: 0; }
.st-addtrack-txt strong { font-size: 13.5px; font-weight: 600; }
.st-addtrack-txt > span { font-size: 12px; opacity: .78; line-height: 1.4; }
/* the lyrics are on the GPU (one AI job per song, owner 2026-09-29): the row is shut and says why */
.st-addtrack:disabled { cursor: not-allowed; }
.st-addtrack.is-blocked { border-style: dotted; background: none; }
.st-addtrack.is-blocked .st-addtrack-plus { opacity: .45; }
.st-addtrack.is-blocked .st-addtrack-txt strong { opacity: .6; }
.st-addtrack.is-blocked .st-addtrack-txt > span { opacity: .9; }
.st-nolyrics .st-cta:disabled { opacity: .5; cursor: not-allowed; }
/* a clone that failed while the window was closed (it closes as the clone starts): the row says so */
.st-addtrack.is-error .st-addtrack-txt > span { opacity: 1; color: color-mix(in srgb, #e0453f 70%, var(--ink)); }
/* a Standard (Spleeter) project (owner 2026-10-01): voice tracks come with Ultra - the row offers it */
.st-addtrack.is-ultra-only { cursor: default; flex-wrap: wrap; align-content: center; row-gap: 12px; }
.st-addtrack.is-ultra-only:hover { background: color-mix(in srgb, var(--accent2, var(--accent, #7c5cff)) 6%, transparent); }
.st-addtrack.is-ultra-only .st-addtrack-txt { flex: 1 1 0; min-width: 160px; }
.st-addtrack-ultra { margin: 0 0 0 auto; flex: none; }
/* a phone: the "+" stays beside the words, the button takes the line under them */
@media (max-width: 560px) {
  .st-addtrack-ultra { margin: 2px 0 0 54px; flex: 1 1 calc(100% - 54px); justify-content: center; }
}
/* In STRIPS style it is a strip of its own: same width, full height, content centred. */
.st-ch.st-ch-addwrap { padding: 0; border: 0; background: none; align-self: stretch; }
.st-ch-addwrap .st-addtrack { height: 100%; min-height: 220px; flex-direction: column; justify-content: center;
                              text-align: center; padding: 16px 12px; }
.st-ch-addwrap .st-addtrack-txt { align-items: center; }

.st-row-id:has(.st-vc-set) { display: flex; align-items: center; gap: 4px; }
/* every strip's name row is as tall as the voice track's settings button, so the strips stay level */
.st-strips .st-ch-top { min-height: 28px; }
.st-vc-set {
  flex: none;
  width: 28px; height: 28px;
  display: grid; place-items: center;
  border: 1px solid var(--stroke);
  border-radius: 8px;
  background: transparent;
  color: inherit;
  cursor: pointer;
}
.st-vc-set:hover { background: var(--hover); }
.st-vc-set:focus-visible { outline: 2px solid var(--accent, #7c5cff); outline-offset: 2px; }

.st-vc-wrap {
  position: fixed;
  inset: 0;
  /* above the cosmic rail (6000) and the tweaks button (6600) — and BELOW the plans (.mzup-overlay 10050) and the
     sign-in (.mzg-overlay 10060), which this window opens: they must paint on top of it, and it is still here when
     they close (2026-09-28; at 2147483000 they opened underneath it) */
  z-index: 10040;
  display: grid;
  place-items: center;
  padding: 16px;
  background: rgba(4, 3, 14, .55);
  backdrop-filter: blur(3px);
}
.st-vc {
  width: min(560px, 100%);
  max-height: min(90dvh, 820px);
  display: flex;
  flex-direction: column;
  border: 1px solid var(--stroke);
  border-radius: 18px;
  background: var(--st-sheet);
  color: var(--ink);
  box-shadow: 0 30px 80px -30px rgba(0, 0, 0, .8);
  overflow: hidden;
}
.st-vc-head {
  display: flex; align-items: center; justify-content: space-between; gap: 10px;
  padding: 10px 10px 10px 16px;
  border-bottom: 1px solid var(--stroke);
}
.st-vc-head h4 { margin: 0; font-size: 16px; font-weight: 650; display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.st-vc-badge {
  font-size: 10.5px; font-weight: 600; letter-spacing: .06em; text-transform: uppercase;
  padding: 3px 8px; border-radius: 999px;
  border: 1px solid color-mix(in srgb, var(--accent2, var(--accent, #7c5cff)) 45%, var(--stroke));
  background: color-mix(in srgb, var(--accent2, var(--accent, #7c5cff)) 14%, transparent);
}
.st-vc-x {
  flex: none;
  width: 44px; height: 44px;
  border: 1px solid var(--stroke); border-radius: 12px;
  background: transparent; color: inherit;
  font: inherit; font-size: 22px; line-height: 1;
  cursor: pointer;
}
.st-vc-x:hover:not(:disabled) { background: var(--hover); }
.st-vc-body {
  overflow-y: auto;
  padding: 14px 16px 18px;
  display: flex;
  flex-direction: column;
  gap: 14px;
  scrollbar-width: thin;
}
.st-vc-intro { margin: 0; font-size: 13.5px; line-height: 1.5; text-wrap: pretty; }
.st-vc-sample,
.st-vc-recorder,
.st-vc-track {
  display: flex;
  flex-direction: column;
  gap: 10px;
  padding: 12px;
  border: 1px solid var(--stroke);
  border-radius: 14px;
  background: var(--panel);
}
.st-vc-sample-id { display: flex; align-items: center; gap: 10px; }
.st-vc-sample-id strong { display: block; font-size: 13.5px; }
.st-vc-sub { display: block; font-size: 12px; opacity: .78; line-height: 1.45; }
.st-vc-dot { flex: none; width: 10px; height: 10px; border-radius: 50%; background: #2faa64;
             box-shadow: 0 0 0 3px rgba(47, 170, 100, .2); }
.st-vc-audio { width: 100%; height: 40px; }
.st-vc-row { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; }
.st-vc-row .st-cta { margin-top: 0; }
.st-vc-btn {
  display: inline-flex; align-items: center; gap: 8px;
  min-height: 40px; padding: 0 14px;
  border: 1px solid var(--stroke); border-radius: 12px;
  background: transparent; color: inherit;
  font: inherit; font-size: 13px;
  cursor: pointer;
}
.st-vc-btn svg { width: 16px; height: 16px; flex: none; }
.st-vc-btn:hover:not(:disabled) { background: var(--hover); }
.st-vc-btn:disabled,
.st-vc .st-cta:disabled { opacity: .5; cursor: not-allowed; }
.st-vc-btn:focus-visible,
.st-vc-seg button:focus-visible,
.st-vc-x:focus-visible { outline: 2px solid var(--accent, #7c5cff); outline-offset: 2px; }
/* a red that keeps its contrast on both themes: mixed toward the ink, darker on light, lighter on dark */
.st-vc-danger { color: color-mix(in srgb, #e0453f 70%, var(--ink)); border-color: color-mix(in srgb, #e0453f 40%, var(--stroke)); }
.st-vc-lead { margin: 0; font-size: 13px; line-height: 1.5; }
.st-vc-read {
  margin: 0;
  padding: 10px 12px;
  border-inline-start: 3px solid var(--accent2, var(--accent, #7c5cff));
  border-radius: 0 10px 10px 0;
  background: color-mix(in srgb, var(--ink) 5%, transparent);
  font-size: 13.5px;
  line-height: 1.6;
  font-style: italic;
}
.st-vc-recrow { display: flex; flex-wrap: wrap; align-items: center; gap: 10px 12px; }
.st-vc-rec {
  display: inline-flex; align-items: center; gap: 8px;
  min-height: 44px; padding: 0 18px;
  border: 2px solid color-mix(in srgb, #ff4d5e 55%, transparent);
  border-radius: 999px;
  background: color-mix(in srgb, #ff4d5e 14%, transparent);
  color: inherit; font: inherit; font-weight: 700;
  cursor: pointer;
}
.st-vc-rec.on { background: color-mix(in srgb, #ff4d5e 26%, transparent); animation: st-pulse 1.4s ease-in-out infinite; }
.st-vc-recdot { width: 12px; height: 12px; border-radius: 50%; background: #ff4d5e; }
.st-vc-recinfo { flex: 1 1 140px; min-width: 120px; display: flex; flex-direction: column; gap: 4px; }
.st-vc-recinfo .st-meter { margin-bottom: 0; }
.st-vc-clock { font-size: 12px; opacity: .8; }
.st-vc-or { font-size: 12px; opacity: .7; }
.st-vc-draft { display: flex; flex-direction: column; gap: 8px; }
.st-vc-consent { display: flex; align-items: flex-start; gap: 10px; min-height: 44px; font-size: 13px; line-height: 1.45;
                 cursor: pointer; }
.st-vc-consent input { flex: none; width: 20px; height: 20px; margin: 1px 0 0; accent-color: var(--accent, #7c5cff); }
.st-vc-track h5 { margin: 0; font-size: 14px; font-weight: 600; }
.st-vc-field { display: flex; flex-direction: column; gap: 6px; }
.st-vc-lbl { font-size: 11.5px; letter-spacing: .04em; text-transform: uppercase; opacity: .72; }
.st-vc-seg { align-self: flex-start; display: inline-flex; flex-wrap: wrap; max-width: 100%;
             border: 1px solid var(--stroke); border-radius: 10px; overflow: hidden; }
.st-vc-seg button {
  min-height: 40px; padding: 0 14px;
  border: 0; border-inline-start: 1px solid var(--stroke);
  background: transparent; color: inherit;
  font: inherit; font-size: 13px;
  cursor: pointer;
}
.st-vc-seg button:first-child { border-inline-start: 0; }
.st-vc-seg button.on { background: color-mix(in srgb, var(--accent, #7c5cff) 16%, transparent); font-weight: 600;
                       box-shadow: inset 0 -2px 0 var(--accent, #7c5cff); }
.st-vc-hint { margin: 0; font-size: 12px; line-height: 1.45; opacity: .78; }
.st-vc-working { display: flex; align-items: center; gap: 12px; padding: 10px 12px; border-radius: 12px;
                 background: color-mix(in srgb, var(--accent, #7c5cff) 10%, transparent); }
.st-vc-working strong { font-size: 13.5px; }
.st-vc-spin {
  flex: none;
  display: inline-block;
  width: 20px; height: 20px;
  border-radius: 50%;
  border: 2.5px solid color-mix(in srgb, currentColor 25%, transparent);
  border-top-color: var(--accent, #7c5cff);
  animation: st-vc-spin .9s linear infinite;
}
@keyframes st-vc-spin { to { transform: rotate(360deg); } }
.st-vc-err { margin: 0; font-size: 12.5px; line-height: 1.45; color: color-mix(in srgb, #e0453f 70%, var(--ink)); }
.st-vc-fine { margin: 0; font-size: 11.5px; line-height: 1.5; opacity: .75; }

@media (max-width: 560px) {
  /* a phone gets the sheet from the bottom, full width, so every control stays within thumb reach */
  .st-vc-wrap { place-items: end center; padding: 0; }
  .st-vc { width: 100%; max-height: 92dvh; border-radius: 18px 18px 0 0; }
  .st-vc-seg { align-self: stretch; }
  .st-vc-seg button { flex: 1 1 0; padding: 0 8px; }
  .st-vc-row .st-cta { flex: 1 1 100%; justify-content: center; }
  /* the recorder wraps here, and a lone "or" at the end of the first line read as a stray word */
  .st-vc-or { display: none; }
}
@media (pointer: coarse) {
  .st-vc-set { width: 40px; height: 40px; }
  .st-strips .st-ch-top { min-height: 40px; }
  .st-vc-btn, .st-vc-seg button { min-height: 44px; }
}
@media (prefers-reduced-motion: reduce) {
  .st-vc-spin, .st-vc-rec.on { animation: none; }
}

/* "My voice": engine switch + diffusion-steps slider (owner 2026-09-28: set the steps yourself, switch engines) */
.st-vc-stepval {
  margin-inline-start: 6px; padding: 1px 8px; border-radius: 999px;
  background: color-mix(in srgb, var(--accent, #7c5cff) 16%, transparent);
  color: var(--ink); font-size: 12px; letter-spacing: 0; text-transform: none; opacity: 1;
}
.st-vc-steprow { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; font-size: 12px; }
.st-vc-steprow > .numeric { opacity: .7; min-width: 2ch; text-align: center; }
.st-vc-range { flex: 1 1 180px; min-width: 140px; height: 32px; accent-color: var(--accent, #7c5cff); }
.st-vc-default { font-size: 12px; }
.st-vc-current { padding: 8px 10px; border-radius: 10px; background: color-mix(in srgb, var(--ink) 5%, transparent); }
@media (pointer: coarse) { .st-vc-range { height: 44px; } }
/* The free plan's allowance (Admin -> Daily Limits -> Free, 2026-09-28): the hints say it before the user asks; past
   it, the plans open (the slider's ceiling, the create button once the song's clones are used). */
.st-vc-plan .st-link { font-size: inherit; }
/* The Pro lock on the create button takes the button's own white: the shared lock purple (css/mz-upsell.css,
   #6d4fc4 in the light theme) vanished on the blue-to-violet gradient. Four classes beat that rule's
   :root[data-theme] one, and mz-upsell.css is injected after this sheet. */
.st-vc .st-vc-track .st-cta .mzg-lockic { color: inherit; }

/* ── names the singer gives the tracks they add (owner 2026-09-28: "so I can be sure which take is which") ──
   The voice desk's "Name this take", the sheet's "Track name", and the in-place rename on a row or strip. Ten
   characters at most (MX_NAME_MAX). */
.st-name-input {
  min-width: 0;
  width: 14ch;
  max-width: 100%;
  min-height: 36px;
  padding: 6px 10px;
  border: 1px solid var(--stroke);
  border-radius: 10px;
  background: color-mix(in srgb, var(--ink, #fff) 5%, transparent);
  color: inherit;
  font: inherit;
  font-size: 13px;
}
.st-name-input::placeholder { color: inherit; opacity: .5; }
.st-name-input:focus { outline: 2px solid var(--accent, #7c5cff); outline-offset: 0; border-color: transparent; }
.st-name-input:disabled { opacity: .55; cursor: not-allowed; }
.st-takename {
  margin: 0 clamp(12px, 1.6vw, 18px);
  display: flex; flex-wrap: wrap; align-items: center; gap: 6px 10px;
}
.st-takename-lbl { font-size: 12.5px; font-weight: 600; }
.st-takename-hint { font-size: 12px; opacity: .7; }
.st-vc-name { align-self: flex-start; width: 16ch; }
/* A name that may be renamed reads as the plain name it is and answers to a click. Size and weight come from
   .st-row-name / .st-ch-name, so only the button's own chrome is reset here. */
.st-name-btn {
  display: block; max-width: 100%; min-width: 0;
  margin: 0; padding: 2px 0;
  border: 0; border-radius: 4px;
  background: none; color: inherit;
  font-family: inherit; letter-spacing: inherit; text-align: start;
  cursor: text;
  text-decoration: underline dotted transparent; text-underline-offset: 3px;
}
.st-name-btn:hover { text-decoration-color: currentColor; }
.st-name-btn:focus-visible { outline: 2px solid var(--accent, #7c5cff); outline-offset: 2px; }
.st-name-edit { width: 11.5ch; min-height: 28px; padding: 2px 6px; border-radius: 6px; font-size: 12.5px; font-weight: 600; }
@media (pointer: coarse) {
  .st-name-btn { min-height: 40px; }
  /* 16px: iOS zooms the page into any smaller field it focuses */
  .st-name-input { min-height: 44px; font-size: 16px; }
  .st-name-edit { min-height: 40px; font-size: 16px; }
}

/* ── Saving a download from the page (phase 9, 2026-09-28; js/vocal-gpu/vocal-components.jsx mzSaveFile) ───────
   Every download is fetched by the page and handed to the browser as a blob, so the confirmation can be honest.
   The popup (.mzdone-*, base styles in css/vocal-cosmic.css) shows the transfer; a track's pill fills with it. */
.st-sr {
  position: absolute; width: 1px; height: 1px; margin: -1px; padding: 0; border: 0;
  overflow: hidden; clip: rect(0 0 0 0); clip-path: inset(50%); white-space: nowrap;
}
.mzdone-icon.is-saving, .mzdone-icon.is-offer {
  background: linear-gradient(135deg, var(--accent, #7c5cff), color-mix(in srgb, var(--accent, #7c5cff) 62%, #000));
  box-shadow: 0 10px 26px -8px color-mix(in srgb, var(--accent, #7c5cff) 70%, transparent);
}
.mzdone-icon.is-failed {
  background: linear-gradient(135deg, #f59e0b, #b45309);
  box-shadow: 0 10px 26px -8px color-mix(in srgb, #b45309 70%, transparent);
}
.mzdone-icon.is-saving svg { animation: mzdone-drop 1.1s ease-in-out infinite; }
@keyframes mzdone-drop { 0%, 100% { transform: translateY(-2px); } 50% { transform: translateY(3px); } }
.mzdone-progress { display: flex; align-items: center; gap: 12px; margin: -6px 0 22px; }
.mzdone-bar {
  position: relative; flex: 1 1 auto; height: 8px; border-radius: 999px; overflow: hidden;
  background: color-mix(in srgb, var(--ink, #fff) 14%, transparent);
}
.mzdone-bar > i {
  display: block; height: 100%; border-radius: inherit; background: var(--accent, #7c5cff);
  transition: width .2s linear;
}
/* no length announced: a sweep instead of a bar that would lie */
.mzdone-bar > i.is-indet { width: 38%; animation: mzdone-sweep 1.2s ease-in-out infinite; }
@keyframes mzdone-sweep { 0% { transform: translateX(-100%); } 100% { transform: translateX(265%); } }
.mzdone-pct {
  flex: none; min-width: 4.5ch; text-align: end; font-size: 13px; font-weight: 700;
  font-variant-numeric: tabular-nums; color: var(--ink, #fff);
}
/* the track's pill: the label stays one short word, the fill carries the progress */
.st-dlpill.is-saving {
  cursor: progress; opacity: 1;
  background: linear-gradient(90deg,
    color-mix(in srgb, var(--accent, #7c5cff) 58%, transparent) var(--p, 0%),
    color-mix(in srgb, var(--accent, #7c5cff) 12%, transparent) var(--p, 0%));
}
.st-dlpill.is-saving:not([style]) { animation: st-dl-pulse 1.4s ease-in-out infinite; }
@keyframes st-dl-pulse { 50% { background-color: color-mix(in srgb, var(--accent, #7c5cff) 30%, transparent); } }
.st-dlpill.is-saved svg { color: #22c55e; }
.st-dlpill.is-saved:hover svg { color: inherit; }
@media (prefers-reduced-motion: reduce) {
  .mzdone-icon.is-saving svg, .mzdone-bar > i.is-indet, .st-dlpill.is-saving:not([style]) { animation: none; }
  .mzdone-bar > i { transition: none; }
}

/* The processing card's four steps on a phone (owner 2026-10-01: "visually not aligned and look like a mess"). The
   shared row wraps 2 + 2 at any width the four labels do not fit, each line centred on its own. Here they are one
   row of four equal columns, the circle above its label, so every step sits in the same place in every language. */
@media (max-width: 640px) {
  .proc-steps {
    display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 2px; align-items: start;
    /* the card's side padding lends the row its room: a 59 px column cut "Separating" in two */
    margin: 16px -16px 0; font-size: 11.5px; line-height: 1.25;
  }
  .proc-steps .s {
    display: flex; flex-direction: column; align-items: center; gap: 6px; min-width: 0;
    text-align: center; overflow-wrap: break-word; hyphens: auto;
  }
  .proc-steps .s .ic { width: 18px; height: 18px; flex: none; }
}
/* a phone held upright: four 52-70 px columns ran long words into each other ("Separating" at 320 px; Russian,
   Finnish, Greek at any phone width) - two aligned rows of two instead, in every language */
@media (max-width: 440px) {
  .proc-steps {
    grid-template-columns: repeat(2, minmax(0, max-content)); justify-content: center;
    column-gap: 22px; row-gap: 10px; margin: 16px 0 0; font-size: 12px;
    align-items: stretch;                  /* both cells of a row as tall: their circles centre on one line */
  }
  .proc-steps .s { flex-direction: row; justify-content: flex-start; text-align: left; gap: 8px; }
}

/* The waveform canvas (owner 2026-10-01: drawn like Audio Tools). It fills its lane; the bars take
   the track's colour through `color`, which the canvas reads when it paints. The lane's playhead and
   unplayed veil still sit on top of it. */
.st-wave-cv { position: absolute; inset: 0; width: 100%; height: 100%; display: block; color: var(--ch, var(--accent)); }
.st-row-wave > .st-wave-cv { z-index: 0; }
.st-ch-wave { position: relative; display: block; }
