/* ── Studio composer: the card surface ──
   `.studio-composer-card` carries no rules here — it is the hook a host scopes
   its own surface tokens on, the way it scopes them on its chat composer, so
   both input cards resolve to the same colour in each theme. The card's own
   colours come from `bg-surface-container-high` + `border-border`. */

/* ── Studio composer: the option band ──
   The control row's middle section scrolls horizontally and must not show a
   scrollbar (it sits 34px tall inside a chat card, where a bar eats a third of
   the row) — neither `scrollbar-width` nor the WebKit pseudo-element has a
   Tailwind utility. The edge fade is a mask whose DEPTH is the actual hidden
   width capped at 32px, written per side by the component's scroll handler, so
   a band with 4px hidden fades 4px and never lies about how much is off-screen.
   `padding: 5px; margin: -5px` keeps a focused pill's ring inside the scroll
   box without moving the row; `scroll-margin-inline` scrolls a focused pill
   fully into view rather than flush against the clipped edge. */
.studio-band {
  --fade-start: 0px;
  --fade-end: 0px;
  padding: 5px;
  margin: -5px;
  scrollbar-width: none;
  -ms-overflow-style: none;
}
.studio-band::-webkit-scrollbar { display: none; }
.studio-band > * { scroll-margin-inline: 10px; }
.studio-band:not([data-overflow='none']) {
  -webkit-mask-image: linear-gradient(
    to right,
    transparent 0,
    #000 var(--fade-start),
    #000 calc(100% - var(--fade-end)),
    transparent 100%
  );
  mask-image: linear-gradient(
    to right,
    transparent 0,
    #000 var(--fade-start),
    #000 calc(100% - var(--fade-end)),
    transparent 100%
  );
}

/* The per-type pills rise in when the media type changes — the control set
   itself changed, and a swap with no motion reads as a repaint glitch. */
@keyframes studio-pill-in {
  from { opacity: 0; transform: translateY(3px); }
  to { opacity: 1; transform: translateY(0); }
}
.studio-pill-in { animation: studio-pill-in 190ms cubic-bezier(0.2, 0.7, 0.3, 1) both; }

@media (prefers-reduced-motion: reduce) {
  .studio-pill-in { animation: none; }
}

/* ── Layer ladder ─────────────────────────────────────────────────────
   PopoverSurface hardcodes z-[1000] (src/web-react/controls.tsx) and a
   className cannot override it (equal specificity → stylesheet order wins).
   The ladder is built AROUND 1000: the viewer sits below it so the
   save-to-vault popover is clickable from inside the viewer; the delete
   confirm sits above the popover; toasts above everything.
   Pinned by tests/studio/layer-order.test.ts. */
.studio-layer-viewer  { z-index: 900; }
.studio-layer-confirm { z-index: 1100; }
.studio-layer-toasts  { z-index: 1200; }

/* ── Grids ── 3px gaps, square corners, no tile border: at a 3px gap a 1px
   border reads as a hairline seam and the grid stops reading as one sheet. */
.studio-grid { display: grid; gap: 3px; }
.studio-grid-library { grid-template-columns: repeat(auto-fill, minmax(230px, 1fr)); }
@media (max-width: 640px) {
  .studio-grid-library { grid-template-columns: repeat(auto-fill, minmax(140px, 1fr)); }
}

/* ── Tile ── the scrim/chip/tooltip colors are theme-INVARIANT literals:
   they sit on generated media and stay dark in both themes. */
.studio-tile { position: relative; isolation: isolate; border-radius: 0; }
.studio-tile-scrim {
  background: linear-gradient(to top,
    rgba(10,10,20,.74) 0%, rgba(10,10,20,.34) 46%,
    rgba(10,10,20,.30) 66%, rgba(10,10,20,.62) 100%);
  opacity: 0; transition: opacity .18s ease; pointer-events: none;
}
.studio-tile:hover .studio-tile-scrim,
.studio-tile:focus-within .studio-tile-scrim,
.studio-tile[data-selected='true'] .studio-tile-scrim { opacity: 1; }
.studio-tile-actions {
  opacity: 0; transform: translateY(-3px);
  transition: opacity .18s ease, transform .18s ease;
}
.studio-tile:hover .studio-tile-actions,
.studio-tile:focus-within .studio-tile-actions { opacity: 1; transform: none; }
.studio-tile-prompt {
  opacity: 0; transform: translateY(3px);
  transition: opacity .18s ease, transform .18s ease;
  text-shadow: 0 1px 2px rgba(0,0,0,.35);
}
.studio-tile:hover .studio-tile-prompt,
.studio-tile:focus-within .studio-tile-prompt { opacity: 1; transform: none; }
.studio-tile.studio-has-player .studio-tile-prompt { left: 36px; }
.studio-ibtn {
  background: rgba(12,13,26,.55); color: #fff;
  -webkit-backdrop-filter: blur(6px); backdrop-filter: blur(6px);
  transition: background .12s ease, color .12s ease;
}
.studio-ibtn:hover { background: rgba(12,13,26,.78); }
.studio-ibtn-danger:hover { color: #ff8095; }
.studio-ibtn::after {
  content: attr(data-tip); position: absolute; top: calc(100% + 6px); right: 0;
  background: rgba(12,13,26,.92); color: #fff;
  font-size: 11px; line-height: 1; padding: 5px 7px; border-radius: 6px;
  white-space: nowrap; opacity: 0; pointer-events: none; transition: opacity .12s ease;
}
.studio-ibtn:hover::after, .studio-ibtn:focus-visible::after { opacity: 1; }
.studio-chip-dark {
  background: rgba(12,13,26,.55); color: #fff;
  -webkit-backdrop-filter: blur(6px); backdrop-filter: blur(6px);
}
.studio-tile-badges > * { transition: opacity .18s ease; }
.studio-tile:hover .studio-tile-badges > :not(.studio-play-btn),
.studio-tile:focus-within .studio-tile-badges > :not(.studio-play-btn) { opacity: 0; }
.studio-tile-sel { display: none; border: 1.5px solid rgba(255,255,255,.9); }
.studio-tile[data-selectable='true']:hover .studio-tile-sel,
.studio-tile[data-selectable='true']:focus-within .studio-tile-sel,
.studio-selectmode .studio-tile-sel { display: grid; }
.studio-selectmode .studio-tile-actions,
.studio-selectmode .studio-play-btn { display: none; }

/* ── Audio waveform ── two identical bar layers; the played layer revealed
   by clip-path from a --pos custom property written per animation frame.
   No CSS animation, so a pause freezes exactly and reduced-motion has
   nothing to strip. */
.studio-wave {
  position: absolute; inset: 0; display: flex; align-items: center;
  justify-content: center; gap: 2px; padding: 0 8%;
}
.studio-wave i {
  flex: 1 1 0; min-width: 2px; max-width: 8px;
  border-radius: 4px; background: currentColor;
}
.studio-wave-layers { position: absolute; inset: 0; }
.studio-wave-play { clip-path: inset(0 calc((1 - var(--pos, 0)) * 100%) 0 0); }
.studio-wave-layers[data-active='true'] .studio-wave-base { opacity: .3; }

/* ── Skeleton ── sweep while running; reduced motion gets a gentle pulse,
   NOT nothing — the surface must still read as busy. */
.studio-skeleton {
  position: relative; overflow: hidden; border-radius: 0;
  background: hsl(var(--accent)); aspect-ratio: var(--r, 1.5);
}
.studio-skeleton::after {
  content: ''; position: absolute; inset: 0;
  background: linear-gradient(100deg, transparent 18%, hsl(var(--foreground) / .07) 50%, transparent 82%);
  transform: translateX(-100%);
  animation: studio-sweep 1.4s ease-in-out infinite;
}
@keyframes studio-sweep { to { transform: translateX(100%); } }
@keyframes studio-softpulse { 0%, 100% { opacity: .25; } 50% { opacity: .7; } }

/* ── Motion ── */
.studio-rise { animation: studio-rise .34s cubic-bezier(.2,.7,.3,1) both; }
@keyframes studio-rise { from { opacity: 0; transform: translateY(8px); } to { opacity: 1; transform: none; } }
.studio-toast-in { animation: studio-toast-in .22s cubic-bezier(.2,.7,.3,1) both; }
.studio-toast-out { animation: studio-toast-out .18s ease both; }
@keyframes studio-toast-in { from { opacity: 0; transform: translateY(10px) scale(.98); } to { opacity: 1; transform: none; } }
@keyframes studio-toast-out { to { opacity: 0; transform: translateY(6px); } }

/* ── Viewer backdrop / dock ── */
.studio-backdrop {
  background: rgba(12,13,26,.46);
  -webkit-backdrop-filter: blur(10px); backdrop-filter: blur(10px);
}
:root[data-theme='dark'] .studio-backdrop,
.dark .studio-backdrop { background: rgba(6,6,12,.62); }
.studio-dock::before {
  content: ''; position: absolute; left: 0; right: 0; bottom: 100%;
  height: 28px; pointer-events: none;
  background: linear-gradient(to top, hsl(var(--background)), transparent);
}
/* --pos is written per animation frame by the shared playback state; the
   fill/thumb consume it here so component TSX carries no var() reference
   (the tokens-contract gate treats TSX var() refs as theme tokens). */
.studio-seek-fill { width: calc(var(--pos, 0) * 100%); }
.studio-seek-thumb { left: calc(var(--pos, 0) * 100%); }
.studio-seek:focus-visible {
  outline: 2px solid hsl(var(--primary)); outline-offset: 3px; border-radius: 999px;
}

@media (prefers-reduced-motion: reduce) {
  .studio-rise, .studio-toast-in, .studio-toast-out { animation: none; }
  .studio-skeleton::after {
    transform: none; background: hsl(var(--foreground) / .07);
    animation: studio-softpulse 1.8s ease-in-out infinite;
  }
}
