/* ===== Fonts (self-hosted, offline) ===== */
@font-face{font-family:'Satoshi';src:url('../assets/fonts/Satoshi-Variable.woff2') format('woff2-variations');font-weight:300 900;font-style:normal;font-display:swap;}
@font-face{font-family:'Chivo Mono';src:url('../assets/fonts/ChivoMono-Regular.woff2') format('woff2');font-weight:400;font-style:normal;font-display:swap;}
@font-face{font-family:'Chivo Mono';src:url('../assets/fonts/ChivoMono-Medium.woff2') format('woff2');font-weight:500;font-style:normal;font-display:swap;}

/* ===== Tokens ===== */
/* Dark-only, aligned to the ctlab.app design system (assets/styles.css). */
:root{
  --accent:#c44dff;          /* GifLab product accent — used ONLY in the tool UI */
  --accent-ink:#0a0a0a;
  --radius:16px; --radius-sm:10px; --radius-pill:999px;
  --mono:'Chivo Mono',ui-monospace,monospace;
  --font-sans:'Satoshi',system-ui,-apple-system,Segoe UI,Roboto,sans-serif;
  --ease:cubic-bezier(.2,.7,.2,1);
  /* site-chrome tokens (nav + footer) copied from ctlab.app */
  --text:#f0f0f0; --text2:#777; --text3:#444; --accent2:#c8bfaf;
  --bg2:#111; --bg3:#1a1a1a; --border:rgba(255,255,255,.07);
  /* tool palette (dark, on the site's #0a0a0a field) */
  --bg:#0a0a0a; --bg-2:#111; --surface:#111; --surface-2:#1a1a1a;
  --ink:#e8e0d0; --ink-2:#a39f95; --ink-3:#6c6a64;
  /* ink-tinted hairlines (canon --border-muted*): color-mix follows --ink → identical to the old
     rgba(232,224,208,.12/.22) since --ink is #e8e0d0. NOT --accent (that's magenta, tool-only). */
  --border-muted:color-mix(in srgb, var(--ink) 12%, transparent); --border-muted-strong:color-mix(in srgb, var(--ink) 22%, transparent);
  --shadow:0 18px 50px rgba(0,0,0,.55);
  --checker-a:#1a1a1a; --checker-b:#101010;
}

*{box-sizing:border-box}
/* The `hidden` attribute must always win over the display rules below
   (.view{display:flex}, .progress{display:grid} would otherwise override it). */
[hidden]{display:none !important}
html,body{height:100%}
html{background:#0a0a0a}
body{
  margin:0; background:#0a0a0a; color:var(--ink);
  font-family:var(--font-sans); font-size:15px; line-height:1.6;
  display:flex; flex-direction:column; min-height:100vh;
  -webkit-font-smoothing:antialiased;
  touch-action:pan-y; /* allow only vertical scroll → no page pinch-zoom, no double-tap-zoom anywhere */
}
/* ambient WebGL field (js/vendor/ctlab-bg.js) — the same background as ctlab.app.
   Falls back to the flat --bg colour if WebGL is unavailable. */
#bg{position:fixed; inset:0; width:100%; height:100%; z-index:-1; display:block; pointer-events:none; background:var(--bg)}
/* Cross-document View Transitions (the site chat enables them in giflab-site.css + assets/styles.css, with
   #bg as a shared element so the field carries over between pages). Dark backdrop belt-and-suspenders: keeps
   the VT overlay from flashing the browser's default white/black in the inter-page gap. Inert if VT is off. */
::view-transition{background:#0a0a0a}
/* chrome + content sit above the field */
nav, main, .site-foot{position:relative; z-index:1}

/* ===== Site nav (copied from ctlab.app assets/styles.css; re-sync on site chrome changes) ===== */
nav{display:flex; align-items:center; justify-content:space-between; padding:14px 40px; position:sticky; top:0; background:transparent; backdrop-filter:blur(14px) saturate(1.15); -webkit-backdrop-filter:blur(14px) saturate(1.15); z-index:100}
.logo{display:flex; align-items:center; cursor:pointer; text-decoration:none}
.logo-wm{height:48px; width:auto; display:block; color:var(--text); transition:opacity .2s}
.logo:hover .logo-wm{opacity:.78}
.nav-links{display:flex; gap:24px; align-items:center}
.nav-links a{font-size:13px; color:var(--text2); transition:color .2s; text-decoration:none}
.nav-links a:not(.nav-cta){position:relative}
.nav-links a:not(.nav-cta)::after{content:''; position:absolute; left:0; bottom:-4px; width:0; height:1px; background:var(--text); transition:width .25s var(--ease)}
.nav-links a:not(.nav-cta):hover::after{width:100%}
.nav-links a:hover{color:var(--text)}
.nav-cta{background:var(--accent2)!important; color:#0a0a0a!important; padding:7px 16px; border-radius:8px; font-size:13px; font-weight:500; transition:opacity .2s,transform .2s}
.nav-cta:hover{opacity:.88; transform:translateY(-1px)}
.nav-toggle{display:none; flex-direction:column; gap:5px; background:none; border:none; cursor:pointer; padding:6px}
.nav-toggle span{width:22px; height:2px; background:var(--text2); border-radius:2px; transition:transform .3s var(--ease),opacity .2s}
.nav-toggle.open span:nth-child(1){transform:translateY(7px) rotate(45deg)}
.nav-toggle.open span:nth-child(2){opacity:0}
.nav-toggle.open span:nth-child(3){transform:translateY(-7px) rotate(-45deg)}

.ghost-btn{
  background:var(--surface); color:var(--ink); border:1px solid var(--border-muted);
  border-radius:var(--radius-pill); width:40px; height:40px; cursor:pointer;
  font-size:17px; display:grid; place-items:center; transition:border-color .2s, transform .15s;
}
.ghost-btn:hover{border-color:var(--border-muted-strong)}
.ghost-btn:active{transform:scale(.94)}
.ghost-btn svg{width:18px; height:18px}

/* ===== Stage / views ===== */
/* wider than the old 1180 to make room for the editor's left tool-rail in the dark gutter;
   the drop & result views are centred, so the extra container width is visually harmless. */
.stage{flex:1; display:flex; flex-direction:column; max-width:1340px; width:100%; margin:0 auto; padding:32px 40px 48px}
.view{flex:1; display:flex; flex-direction:column}

/* ===== Drop ===== */
.drop-view{align-items:center; justify-content:center; gap:18px}
.tool-hero{display:flex; flex-direction:column; align-items:center; gap:10px; text-align:center; margin-bottom:8px}
.tool-hero .th-wm{height:56px; width:auto; color:var(--ink)}
.tool-hero .th-sub{max-width:460px; color:var(--text2); font-size:15px; line-height:1.65}
.dropzone{
  width:min(620px,92vw); padding:60px 30px; border-radius:24px;
  border:2px dashed var(--border-muted-strong); background:var(--surface);
  display:flex; flex-direction:column; align-items:center; gap:8px; text-align:center;
  cursor:pointer; transition:border-color .2s, background .2s, transform .2s;
}
.dropzone:hover,.dropzone:focus-visible{border-color:var(--accent); outline:none; transform:translateY(-2px)}
.dropzone.dragover{border-color:var(--accent); background:color-mix(in srgb,var(--accent) 8%,var(--surface))}
.dz-mark{color:var(--accent); line-height:0}
.dz-mark svg{width:46px; height:46px}
.dz-title{font-size:22px; font-weight:600}
.dz-sub{color:var(--ink-2)}
.dz-privacy{margin-top:10px; font-family:var(--font-sans); font-size:11px; letter-spacing:.06em; color:var(--ink-3)}
.formats{font-family:var(--font-sans); font-size:11px; color:var(--ink-3); letter-spacing:.04em}

/* ===== Editor ===== */
/* 2 cols: the controls panel (which carries its OWN left gutter for per-control help — see .controls
   below) and the preview. The help sits in that gutter, on the page's black bg, across from each control. */
.editor{display:grid; grid-template-columns:[ctl]minmax(0,500px) [preview]minmax(0,1fr); gap:22px; align-items:start; max-width:1340px; margin:0 auto; width:100%}
.preview-col{order:2; display:flex; flex-direction:column; gap:14px; min-width:0}
.controls{order:1}
/* palette column + preview side by side */
.stage-row{display:flex; gap:10px; align-items:flex-start; min-width:0}
.stage-wrap{
  flex:1; min-width:0;
  position:relative; border-radius:var(--radius); overflow:hidden;
  border:1px solid var(--border-muted);   /* persistent frame so the FIXED box reads at the same size whether
                                              empty or loaded — the letterboxed video sits inside it, not floating
                                              (empty state overrides this with the dashed dropzone border below) */
  background:
    repeating-conic-gradient(var(--checker-a) 0% 25%, var(--checker-b) 0% 50%) 0 / 22px 22px;
  display:flex; align-items:center; justify-content:center;
  height:clamp(240px,56vh,560px);   /* fixed preview area — the frame fits INSIDE it, so changing the
                                       aspect reshapes the frame without moving/resizing the window */
  cursor:grab;
}
.stage-wrap.rotating{cursor:grabbing}
/* preview + its caption share a column so the caption aligns to the preview's LEFT edge, not the panel's */
.stage-main{display:flex; flex-direction:column; gap:6px; flex:1; min-width:0}
.stage-main>.stage-wrap{flex:none}   /* keep the clamp height — column flex-grow (flex:1) would zero the basis and collapse it */
/* name of the clip currently loaded — a caption UNDER the preview in a RESERVED, fixed-height slot, so
   showing/hiding it never nudges the preview. Left-anchored across the preview width; overflow feathers
   out softly on the right (mask) rather than a hard ellipsis. */
.stage-name{align-self:stretch; height:17px; flex:none; display:flex; align-items:center; gap:7px; margin:0;
  font-family:var(--font-sans); font-size:12px; font-weight:600; color:var(--ink-2); min-width:0;
  overflow:hidden; white-space:nowrap;
  -webkit-mask-image:linear-gradient(90deg,#000 calc(100% - 30px),#0000); mask-image:linear-gradient(90deg,#000 calc(100% - 30px),#0000)}
.stage-name svg{flex:none; width:14px; height:14px; opacity:.8; color:var(--accent)}
.stage-name span{overflow:hidden; text-overflow:clip; white-space:nowrap; min-width:0}
/* persistent hint UNDER the preview (outside the window, so it never covers the video) */
.stage-tip{align-self:center; display:inline-flex; align-items:center; gap:6px; margin-top:-4px;
  font-family:var(--font-sans); font-size:11px; font-weight:500; color:var(--ink-3)}
.stage-tip svg{width:13px; height:13px; opacity:.8}
/* LOCAL-ONLY dev URL bar (yt-dlp via serve.py /grab) — unhidden by JS on localhost only, never public */
.stage-url{align-self:center; display:inline-flex; align-items:center; gap:8px; margin-top:2px; max-width:100%}
.stage-url .su-badge{font-family:var(--mono); font-size:9px; font-weight:700; letter-spacing:.08em; text-transform:uppercase;
  color:var(--accent); border:1px solid color-mix(in srgb,var(--accent) 45%,transparent); border-radius:5px; padding:2px 5px; opacity:.85}
.stage-url .su-input{width:320px; max-width:48vw; font-family:var(--font-sans); font-size:12px; color:var(--text);
  background:var(--bg2); border:1px solid var(--border); border-radius:7px; padding:7px 10px; outline:none; transition:border-color .15s}
.stage-url .su-input:focus{border-color:var(--accent)}
.stage-url .su-input::placeholder{color:var(--ink-3)}
.stage-url .su-grab{font-family:var(--font-sans); font-size:12px; font-weight:600; color:#fff; cursor:pointer;
  background:var(--accent); border:0; border-radius:7px; padding:7px 14px; transition:transform .12s, filter .15s}
.stage-url .su-grab:hover{filter:brightness(1.08)}
.stage-url .su-grab:active{transform:scale(.95)}
.stage-url .su-grab:disabled{opacity:.55; cursor:default; transform:none; filter:none}
.stage-url .su-status{font-family:var(--font-sans); font-size:11px; color:var(--ink-3); min-height:1em}
.stage-url .su-status.err{color:#ff6b6b}
/* DESKTOP-app only: Instagram cookie source picker (shown by JS when pywebview is present) */
.ig-connect{align-self:center; display:inline-flex; align-items:center; gap:8px; margin-top:2px;
  font-family:var(--font-sans); font-size:12px; color:var(--ink-2)}
.ig-connect .ig-glyph{width:16px; height:16px; color:var(--accent); flex:none}
.ig-connect .ig-cap{font-weight:600; color:var(--ink-2)}
.ig-connect .ig-btns{display:inline-flex; gap:6px; flex-wrap:wrap; align-items:center}
.ig-connect .ig-or{font-size:11px; color:var(--ink-3); margin:0 2px}
.ig-connect .ig-btns button{font-family:var(--font-sans); font-size:12px; font-weight:600; color:var(--text); cursor:pointer;
  background:var(--bg2); border:1px solid var(--border); border-radius:7px; padding:5px 11px;
  transition:transform .12s, filter .15s, border-color .15s, background .15s, color .15s}
.ig-connect .ig-btns button:hover{border-color:var(--accent); color:#fff}
.ig-connect .ig-btns button:active{transform:scale(.95)}
.ig-connect .ig-btns button.primary{background:var(--accent); border-color:var(--accent); color:#fff}
.ig-connect .ig-btns button:disabled{opacity:.55; cursor:default; transform:none}
.ig-connect .ig-status{font-size:11px; color:var(--ink-3)}
.ig-connect .ig-status.ok{color:#46d39a}
.ig-connect .ig-status.err{color:#ff6b6b}
/* drop target shown while a file is dragged over the editor (click never opens a picker — drag only) */
.stage-drop{position:absolute; inset:0; z-index:6; display:none; flex-direction:column; align-items:center; justify-content:center; gap:12px;
  background:color-mix(in srgb,var(--accent) 20%,rgba(10,10,10,.62)); border:2.5px dashed var(--accent); border-radius:inherit;
  color:#fff; font-family:var(--font-sans); font-weight:600; font-size:15px; pointer-events:none; -webkit-backdrop-filter:blur(2px); backdrop-filter:blur(2px)}
.stage-drop svg{width:44px; height:44px}
.stage-wrap.drag-over .stage-drop{display:flex}

/* empty state: no clip loaded → the preview IS the dropzone (pulses to invite a drop), and the rest of
   the editor chrome hides until a video is loaded. */
.stage-empty{display:none; position:absolute; inset:0; z-index:5; flex-direction:column; align-items:center; justify-content:center;
  width:100%; border:0; background:transparent; color:var(--ink-2); font-family:var(--font-sans); cursor:pointer; text-align:center; padding:20px}
.se-mark{color:var(--accent); line-height:0; margin-bottom:8px}
.se-mark svg{width:42px; height:42px}
.se-title{font-size:21px; font-weight:600; color:var(--ink)}
.se-sub{font-size:13px; color:var(--ink-2); margin-top:4px}
.se-privacy{margin-top:12px; font-family:var(--mono); font-size:11px; letter-spacing:.05em; color:var(--ink-3)}
/* the full editor is shown right away; only the preview is empty (a pulsing dropzone) until a clip loads */
body.no-clip .stage-empty{display:flex}
body.no-clip .stage-wrap{cursor:pointer; border:2px dashed var(--border-muted-strong); animation:dzPulse 2.4s ease-in-out infinite}
@keyframes dzPulse{
  0%,100%{border-color:var(--border-muted-strong); box-shadow:0 0 0 0 transparent}
  50%{border-color:var(--accent); box-shadow:0 0 0 4px color-mix(in srgb,var(--accent) 16%,transparent)}
}
body.no-clip .close-btn{display:none}
/* stage-tip + stage-name + snap-row RESERVE their space when empty (visibility, not display:none) so loading a
   clip doesn't grow the preview column and shove the timeline/controls/footer down (owner: "окно увеличивается
   вниз, элементы отскакивают"). The box itself is already fixed-height; this stabilises the column below it. */
body.no-clip .stage-tip{visibility:hidden}
body.no-clip .stage-name{visibility:hidden}
/* rotation wrapper: .rot takes layout space (sized by JS to the rotated footprint);
   .rot-inner is centered + rotated and holds the video + crop overlay together. */
/* translateZ(0) forces iOS Safari to actually clip the transformed (zoomed/rotated) video to the frame
   — without it overflow:hidden leaks the video outside the frame on iOS. */
/* flex:none so view-zoom > 100% can overflow the (centered) stage and be clipped, instead of the flex
   container shrinking the frame back to fit */
.rot{position:relative; overflow:hidden; transform:translateZ(0); flex:none}
.rot-inner{position:absolute; top:50%; left:50%; transform:translate(-50%,-50%); line-height:0; transition:transform .12s linear}
/* smooth the size/position change on a Fit/Fill toggle so the video glides instead of jumping */
.rot-inner.animating{transition:transform .2s ease, width .2s ease, height .2s ease, left .2s ease, top .2s ease}
/* during a drag/gesture: no transition, so the video follows the fingers instantly (no lag/jerk) */
.rot-inner.dragging{transition:none}
#video{display:block; width:100%; height:100%; object-fit:contain}

/* crop overlay — axis-aligned over the rotated-video footprint (.rot); positioned in %.
   The bare layer (outside the box) captures rotate-drags; the box itself moves/resizes. */
.crop-layer{position:absolute; inset:0; pointer-events:auto; cursor:grab}
.crop-layer.rotating{cursor:grabbing}
.crop-box{
  position:absolute; left:0; top:0; width:100%; height:100%;
  border:1.5px solid #fff; box-shadow:0 0 0 9999px rgba(0,0,0,.5); pointer-events:auto; cursor:move;
  touch-action:none; /* drag the crop with a finger without scrolling the page */
}
.crop-box::before{content:""; position:absolute; inset:0;
  background:
    linear-gradient(var(--accent),var(--accent)) 33.3% 0/1px 100% no-repeat,
    linear-gradient(var(--accent),var(--accent)) 66.6% 0/1px 100% no-repeat,
    linear-gradient(var(--accent),var(--accent)) 0 33.3%/100% 1px no-repeat,
    linear-gradient(var(--accent),var(--accent)) 0 66.6%/100% 1px no-repeat;
  opacity:.5}
.ch{position:absolute; width:14px; height:14px; background:#fff; border:2px solid var(--accent); border-radius:3px; pointer-events:auto; touch-action:none}
.ch-nw{left:-7px; top:-7px; cursor:nwse-resize}
.ch-ne{right:-7px; top:-7px; cursor:nesw-resize}
.ch-sw{left:-7px; bottom:-7px; cursor:nesw-resize}
.ch-se{right:-7px; bottom:-7px; cursor:nwse-resize}
/* full-length edge grab bars (drag any border, not just a midpoint) */
.ce{position:absolute; pointer-events:auto; touch-action:none}
.ce-n{top:-5px; left:10px; right:10px; height:11px; cursor:ns-resize}
.ce-s{bottom:-5px; left:10px; right:10px; height:11px; cursor:ns-resize}
.ce-e{right:-5px; top:10px; bottom:10px; width:11px; cursor:ew-resize}
.ce-w{left:-5px; top:10px; bottom:10px; width:11px; cursor:ew-resize}

/* transport */
.transport{display:flex; align-items:center; gap:12px}

/* preview view-zoom bar (under the stage, AE-style): scales the whole comp in the window, view-only */
.view-bar{display:flex; align-items:center; gap:10px; padding:0 2px}
.vb-label{flex:none; display:inline-flex; align-items:center; gap:5px; font-family:var(--font-sans); font-size:11px; letter-spacing:.06em; text-transform:uppercase; color:var(--ink-3)}
.vb-label svg{width:13px; height:13px; opacity:.85}
/* deliberately short (and higher-specificity than .range's width:100%) so it doesn't read like the
   playback scrubber below it */
.view-bar .vb-range{flex:none; width:132px}
.vb-sel{flex:none; background:var(--surface-2); border:1px solid var(--border-muted); color:var(--ink); border-radius:var(--radius-sm); padding:5px 8px; font-family:var(--font-sans); font-size:12px; cursor:pointer}
.vb-sel:focus{outline:none; border-color:var(--accent)}
.vb-loop{flex:none; display:grid; place-items:center; width:30px; height:30px; padding:0; border-radius:8px;
  background:var(--surface-2); border:1px solid var(--border-muted); color:var(--ink-2); cursor:pointer; transition:background .15s, color .15s, border-color .15s, transform .12s}
.vb-loop svg{width:16px; height:16px}
.vb-loop:hover{color:var(--ink); border-color:var(--accent)}
.vb-loop:active{transform:scale(.92)}
.vb-loop.active{background:var(--accent); color:var(--accent-ink); border-color:color-mix(in srgb,var(--accent) 55%,#fff)}
.vb-vol{display:inline-flex; align-items:center; gap:6px}
.vb-vol .vb-vol-range{flex:none; width:78px}
.vb-vol .vb-loop.muted{color:var(--ink-3)}

/* mobile-only gesture hint under the player (desktop hides it) */
.gesture-hint{display:none}
/* snapshot button (right under the timeline): saves the current frame as a PNG to Downloads */
.snap-row{display:flex; justify-content:center; padding:2px}
.snap-btn{display:inline-flex; align-items:center; gap:7px; padding:7px 14px; border-radius:var(--radius-pill);
  background:var(--surface-2); border:1px solid var(--border-muted); color:var(--ink-2); font-family:var(--font-sans); font-size:12px; font-weight:500;
  cursor:pointer; transition:border-color .15s, color .15s, transform .12s}
.snap-btn svg{width:15px; height:15px}
.snap-btn:hover{border-color:var(--accent); color:var(--ink)}
.snap-btn:active{transform:scale(.96)}
body.no-clip .snap-row{visibility:hidden}

/* under-playback block: keyboard reference (2 columns) + the preview-tool legend (moved here from the
   old left rail). Both sit beneath the player on desktop; hidden on touch (gestures, no keyboard). */
.under-stage{display:flex; flex-direction:column; gap:14px; padding:2px}
.shortcuts{display:grid; grid-template-columns:repeat(2,max-content); gap:8px 30px; padding:0; margin:0}
.sc{display:inline-flex; align-items:center; gap:5px; font-family:var(--font-sans); font-size:11px; color:var(--ink-3)}
.sc kbd{font-family:var(--font-sans); font-size:10px; color:var(--ink-2); background:var(--surface-2);
  border:1px solid var(--border-muted); border-bottom-width:2px; border-radius:5px; padding:1px 5px; line-height:1.5}
.play{flex:none}
.tcode{font-family:var(--font-sans); font-size:12px; color:var(--ink-2); min-width:52px; text-align:center}
/* DaVinci-style: a "video strip" along the bottom you scrub freely, with the In/Out flags up in a
   separate top lane so they never sit on top of the playhead. Track/selection/playhead don't capture
   pointer events — only the flags do — so clicking the strip always moves the playhead, even at In/Out. */
/* the timeline element IS the strip (so the play button centres on it via align-items:center); the
   In/Out flags poke up into a lane ABOVE it (overflow:visible), kept off the playhead/scrub area. */
.timeline{position:relative; flex:1; height:26px; cursor:pointer; touch-action:none; user-select:none; -webkit-user-select:none; overflow:visible}
.timeline *{-webkit-user-drag:none}
.tl-track{position:absolute; inset:0; border-radius:7px; background:var(--surface-2); border:1px solid var(--border-muted); pointer-events:none}
.tl-selection{position:absolute; top:0; bottom:0; box-sizing:border-box; border-radius:5px; background:color-mix(in srgb,var(--accent) 22%,transparent); border:1px solid var(--accent); pointer-events:none}
.tl-handle{position:absolute; top:-18px; transform:translateX(-50%); width:18px; height:16px; cursor:ew-resize; z-index:3}
.tl-handle::before{content:''; position:absolute; left:50%; top:0; transform:translateX(-50%); width:14px; height:14px; border-radius:4px; background:var(--accent); box-shadow:0 1px 4px rgba(0,0,0,.45)}
.tl-handle::after{content:''; position:absolute; left:50%; top:9px; height:35px; transform:translateX(-50%); width:2px; background:var(--accent); opacity:.85; pointer-events:none}
.tl-playhead{position:absolute; top:-18px; height:calc(100% + 18px); transform:translateX(-50%); width:2px; background:var(--ink); pointer-events:none; opacity:.95; z-index:4; box-shadow:0 0 0 1px rgba(0,0,0,.5)}

/* ===== Controls ===== */
.controls{
  background:var(--surface); border:1px solid var(--border-muted); border-radius:var(--radius);
  padding:18px; display:flex; flex-direction:column; gap:18px; position:sticky; top:90px;
  /* the app-shell scrolls this panel internally (preview stays pinned) — keep that scrollbar slim */
  scrollbar-width:thin; scrollbar-color:var(--border-muted-strong) transparent;
}
.controls::-webkit-scrollbar{width:8px}
.controls::-webkit-scrollbar-thumb{background:var(--border-muted-strong); border-radius:8px}
.controls::-webkit-scrollbar-track{background:transparent}
/* page-wide slim dark scrollbar — the default browser one clashed with the dark theme */
html{scrollbar-width:thin; scrollbar-color:var(--border-muted-strong) transparent}
::-webkit-scrollbar{width:11px; height:11px}
::-webkit-scrollbar-track{background:transparent}
::-webkit-scrollbar-thumb{background:var(--border-muted-strong); border-radius:8px; border:3px solid transparent; background-clip:padding-box}
::-webkit-scrollbar-thumb:hover{background:color-mix(in srgb,var(--border-muted-strong) 55%,#fff)}
.ctl-group{display:flex; flex-direction:column; gap:8px}
/* staggered slide-in for tool groups on editor-entry + format/mode switch (driven from JS). */
@keyframes ctlIn{from{opacity:0; transform:translateY(9px)} to{opacity:1; transform:none}}
/* Height-glide wrapper (mirrors the site's .collapsible-inner): the glide animates THIS wrapper's
   height with overflow clipping, so the group itself is never clipped — the absolutely-positioned
   gutter help (containing block = the group) escapes the clip and simply fades with the group's
   opacity. Nothing gets sliced mid-glide. */
/* min-height:0 lets it collapse under grid 0fr; overflow is toggled to hidden by JS ONLY during a
   glide — leaving it permanently on would clip slider thumbs (they overflow their 6px track and sit
   at the group's bottom edge, the gutter help being absolute). */
.glide-inner{min-height:0}
.ctl-group>.glide-inner{display:flex; flex-direction:column; gap:8px}   /* carry the group's column layout */
.ctl-label{font-family:var(--font-sans); font-size:11px; letter-spacing:.08em; text-transform:uppercase; color:var(--ink-2); display:flex; justify-content:space-between; align-items:center}
.ctl-label b{color:var(--ink); font-weight:600; letter-spacing:0; text-transform:none}
.ctl-row{display:flex; gap:10px}
.ctl-row label{flex:1; display:flex; align-items:center; gap:6px; font-size:12px; color:var(--ink-2)}
.num{width:100%; background:var(--surface-2); border:1px solid var(--border-muted); color:var(--ink); border-radius:var(--radius-sm); padding:7px 8px; font-family:var(--font-sans); font-size:12px}
.num:focus{outline:none; border-color:var(--accent)}
.hint{font-family:var(--font-sans); font-size:11px; color:var(--ink-3)}
/* per-control inline help — base (mobile): a line right under each control group */
.m-help{display:block; margin:4px 0 0; font-family:var(--font-sans); font-size:12px; line-height:1.5; color:var(--ink-2); font-weight:500}
.m-help b{font-weight:700; color:var(--ink)}
/* UI is Satoshi now; keep live-changing numbers on tabular figures so digits don't jitter as they tick */
.tcode,.vb-sel,.num,.ctl-label b,.hint,.est-geom,.est-size,.result-meta,.progress-label,.zoom-field input{font-variant-numeric:tabular-nums}
@media(min-width:981px){
  /* desktop: controls drop their card; each help floats into a left gutter on the page's black bg,
     top-aligned ACROSS FROM its own control (not stacked under it). */
  .controls{background:transparent; border:0; padding:0 0 0 190px; gap:26px}
  .ctl-group{position:relative}
  /* descendant (not >) so the Crop help nested inside #framePanel also floats into the gutter;
     left-inset from the panel edge so the first glyph isn't clipped by the panel's overflow:hidden */
  .ctl-group .m-help{position:absolute; right:100%; top:1px; width:160px; margin:0 18px 0 0; font-size:11.5px; line-height:1.45}
}
/* per-parameter reset (↺) buttons in control labels + the right-side group of label value+reset */
.lbl-r{display:inline-flex; align-items:center; gap:8px}
.rst{display:inline-grid; place-items:center; width:24px; height:24px; padding:0; flex:none; text-transform:none; letter-spacing:0;
  background:var(--surface-2); border:1px solid var(--border-muted); border-radius:7px; color:var(--ink-2); cursor:pointer;
  transition:border-color .15s, color .15s, transform .25s var(--ease)}
.rst:hover{border-color:var(--accent); color:var(--ink)}
.rst:active{transform:rotate(-150deg) scale(.9)}
.rst svg{width:13px; height:13px}
.reset-all{display:inline-flex; align-items:center; justify-content:center; gap:7px}
.reset-all svg{width:14px; height:14px; transition:transform .25s var(--ease)}
.reset-all:active svg{transform:rotate(-150deg)}
/* inline numeric zoom field inside the Zoom label (type a value, or use the slider) */
.zoom-field{display:inline-flex; align-items:center; gap:2px; color:var(--ink); font-weight:600; text-transform:none; letter-spacing:0}
.zoom-field input{width:44px; background:var(--surface-2); border:1px solid var(--border-muted); color:var(--ink);
  border-radius:6px; padding:2px 5px; font-family:var(--font-sans); font-size:12px; font-weight:600; text-align:right; -moz-appearance:textfield}
.zoom-field input:focus{outline:none; border-color:var(--accent)}
.zoom-field input::-webkit-outer-spin-button,.zoom-field input::-webkit-inner-spin-button{-webkit-appearance:none; margin:0}

.seg{position:relative; display:flex; gap:5px; background:var(--surface-2); padding:5px; border-radius:var(--radius-pill); border:1px solid var(--border-muted)}
.seg .thumb{position:absolute; top:5px; bottom:5px; left:0; width:0; border-radius:var(--radius-pill); background:var(--accent); z-index:0;
  box-shadow:0 2px 10px color-mix(in srgb,var(--accent) 45%,transparent);
  transition:transform .42s cubic-bezier(.33,.7,.3,1), width .42s cubic-bezier(.33,.7,.3,1), background .2s}
.seg-btn{position:relative; z-index:1; flex:1; background:transparent; border:0; color:var(--ink-2); padding:7px 8px; border-radius:var(--radius-pill); cursor:pointer; font-family:var(--font-sans); font-weight:500; font-size:13px; white-space:nowrap; transition:color .2s, transform .12s}
.seg-btn:hover{color:var(--ink)}
.seg-btn:active{transform:scale(.95)}
.seg-btn.active{color:var(--accent-ink); font-weight:600}
#ratioPanel, #framePanel{display:flex; flex-direction:column; gap:8px; margin-top:8px}
#orientSeg .seg-btn{font-size:12px}
.ratio-pills .seg-btn{font-family:var(--font-sans); font-size:11.5px}
/* align grid (left) + frame-color tile (right) share a row, matched in height */
.align-row{display:flex; align-items:stretch; gap:12px}
.frame-color{flex:1; min-width:0; display:flex; flex-direction:column; align-items:center; justify-content:center; gap:7px; cursor:pointer;
  background:var(--surface-2); border:1px solid var(--border-muted); border-radius:var(--radius-sm); padding:8px; transition:border-color .15s;
  font-family:var(--font-sans); font-size:11px; color:var(--ink-2); text-align:center}
.frame-color:hover{border-color:var(--accent)}
.frame-color input[type=color]{flex:1; width:100%; min-height:0; border:1px solid var(--border-muted-strong); border-radius:7px; background:none; padding:0; cursor:pointer}
.frame-color span{flex:none}
.align-grid{flex:none; display:grid; grid-template-columns:repeat(3,1fr); gap:4px; background:var(--surface-2); border:1px solid var(--border-muted); border-radius:var(--radius-sm); padding:4px; width:96px}
.align-grid button{position:relative; aspect-ratio:1; border:0; background:transparent; border-radius:6px; cursor:pointer; transition:background .15s}
.align-grid button::after{content:''; position:absolute; inset:34%; border-radius:50%; background:var(--ink-3); transition:background .15s}
.align-grid button:hover{background:var(--surface)}
.align-grid button.active{background:var(--accent)}
.align-grid button.active::after{background:var(--accent-ink)}

/* remove-video button on the preview */
.close-btn{position:absolute; top:10px; right:10px; z-index:5; width:34px; height:34px; border-radius:var(--radius-pill);
  background:color-mix(in srgb,var(--bg) 55%,transparent); color:#fff; border:1px solid rgba(255,255,255,.28);
  -webkit-backdrop-filter:blur(6px); backdrop-filter:blur(6px); display:grid; place-items:center; cursor:pointer;
  transition:background .15s, transform .12s}
.close-btn:hover{background:color-mix(in srgb,var(--bg) 25%,transparent)}
.close-btn:active{transform:scale(.92)}
.close-btn svg{width:16px; height:16px}

/* ===== Tool palette (After Effects-style: Move / Rotate / Zoom) ===== */
/* the palette sits in its own column beside the preview (off the image, never covering it) */
.tool-palette{display:flex; flex-direction:column; gap:6px; flex:none; align-self:flex-start;
  padding:6px; border-radius:14px; background:var(--surface); border:1px solid var(--border-muted);
  box-shadow:0 8px 24px rgba(0,0,0,.35)}
.tool-btn{width:34px; height:34px; display:grid; place-items:center; border-radius:9px;
  background:transparent; border:1px solid transparent; color:var(--ink-2); cursor:pointer;
  transition:background .15s, color .15s, border-color .15s, transform .12s}
.tool-btn svg{width:18px; height:18px; transition:transform .2s var(--ease)}
.tool-btn:hover{color:var(--ink); background:rgba(255,255,255,.07)}
.tool-btn:hover svg{transform:scale(1.1)}
.tool-btn:active{transform:scale(.9)}
.tool-btn.active{background:var(--accent); color:var(--accent-ink); border-color:color-mix(in srgb,var(--accent) 55%,#fff)}
.tool-btn.active svg{transform:scale(1.06)}
.tool-btn[data-tool="rotate"].active svg{animation:toolspin .5s var(--ease)}
@keyframes toolspin{from{transform:rotate(-110deg) scale(1.06)}to{transform:rotate(0) scale(1.06)}}

/* ===== Tool legend (under the playback — explains the preview tools: Move / Rotate / Zoom) ===== */
.tool-legend{display:grid; grid-template-columns:repeat(3,1fr); gap:10px}
/* static info cards — explanation only (tool switching lives on the on-preview palette + V/R/Z keys) */
.tl-item{display:flex; align-items:center; gap:10px; width:100%; text-align:left;
  background:var(--surface-2); border:1px solid var(--border-muted); border-radius:var(--radius-sm); padding:8px 10px; color:var(--ink-2)}
.tl-ic{flex:none; width:28px; height:28px; display:grid; place-items:center; border-radius:7px;
  background:var(--surface); border:1px solid var(--border-muted); color:var(--ink-2)}
.tl-ic svg{width:16px; height:16px}
.tl-txt{display:flex; flex-direction:column; line-height:1.25; flex:1; min-width:0}
.tl-txt b{font-size:13px; font-weight:600; color:var(--ink)}
.tl-txt i{font-style:normal; font-size:11px; color:var(--ink-3)}
.tl-item kbd{flex:none; font-family:var(--font-sans); font-size:10px; color:var(--ink-3);
  background:var(--surface); border:1px solid var(--border-muted); border-bottom-width:2px; border-radius:5px; padding:2px 6px}


.range{-webkit-appearance:none; appearance:none; width:100%; height:6px; border-radius:var(--radius-pill); background:var(--surface-2); border:1px solid var(--border-muted); outline:none}
.range::-webkit-slider-thumb{-webkit-appearance:none; width:18px; height:18px; border-radius:50%; background:var(--accent); cursor:pointer; border:3px solid var(--surface)}
.range::-moz-range-thumb{width:16px; height:16px; border-radius:50%; background:var(--accent); cursor:pointer; border:3px solid var(--surface)}
/* bitrate row: slider + direct numeric entry (owner: type an exact Mbps, up to 100) */
.br-row{display:flex; align-items:center; gap:10px}
.br-row .range{flex:1; min-width:0}
.br-num{flex:none; width:58px; background:var(--surface-2); border:1px solid var(--border-muted); color:var(--ink); border-radius:var(--radius-sm); padding:6px 8px; font-family:'Chivo Mono',ui-monospace,monospace; font-size:13px; font-weight:600; text-align:center; -moz-appearance:textfield; appearance:textfield}
.br-num:focus{outline:none; border-color:var(--accent)}
/* kill the native number-input spinner arrows (owner: "мерзкие стрелки") — slider is the primary control */
.br-num::-webkit-inner-spin-button,.br-num::-webkit-outer-spin-button{-webkit-appearance:none; margin:0}
.br-unit{flex:none; color:var(--ink-3); font-size:12px; font-family:'Chivo Mono',ui-monospace,monospace}

.check{display:flex; align-items:center; gap:9px; font-size:14px; color:var(--ink); cursor:pointer}
.check input{accent-color:var(--accent); width:17px; height:17px}

.estimate{display:flex; flex-direction:column; gap:4px; align-items:center; text-align:center; border-top:1px solid var(--border-muted); padding-top:14px}
.est-geom{font-family:var(--font-sans); font-size:11px; color:var(--ink-3)}
.est-size{font-family:var(--font-sans); font-size:13px; color:var(--ink); font-weight:500}
.est-size b{color:var(--accent); font-weight:600}

/* buttons */
.primary-btn{
  background:var(--accent); color:var(--accent-ink); border:0; border-radius:var(--radius-sm);
  padding:13px 16px; font-family:var(--font-sans); font-weight:600; font-size:15px; cursor:pointer;
  text-align:center; text-decoration:none; display:block; transition:filter .15s, transform .12s;
}
.primary-btn:hover{filter:brightness(1.08)}
.primary-btn:active{transform:scale(.985)}
/* Feedback CTA under Export — same shape, but a MUTED magenta fill (not the solid accent) so it reads as a
   secondary "leave feedback" action, not the primary Export CTA. Web-only. Opens the existing #fbModal. */
.feedback-btn{display:flex; align-items:center; justify-content:center; gap:8px; width:100%;
  background:color-mix(in srgb, var(--accent) 13%, var(--surface-2)); color:var(--accent);
  border:1px solid color-mix(in srgb, var(--accent) 38%, transparent); border-radius:var(--radius-sm);
  padding:12px 16px; font-family:var(--font-sans); font-weight:600; font-size:14px; cursor:pointer;
  transition:background .18s, border-color .18s, transform .12s}
.feedback-btn svg{width:16px; height:16px; flex:none}
.feedback-btn:hover{background:color-mix(in srgb, var(--accent) 22%, var(--surface-2)); border-color:var(--accent)}
.feedback-btn:active{transform:scale(.985)}
html.desktop-app .feedback-btn{display:none}   /* web-only — the desktop app has its own feedback channel */
/* Trim In/Out values fly into their fields when a clip loads (owner ref: the auth-form field reveal on
   Sign in↔Create account) — the numbers glide in instead of just snapping/reloading. */
@keyframes trimFlyIn{from{opacity:0; transform:translateY(7px)} to{opacity:1; transform:translateY(0)}}
.fly-in{animation:trimFlyIn .28s cubic-bezier(.16,1,.3,1) both}
.text-btn{background:transparent; border:0; color:var(--ink-2); cursor:pointer; font-family:var(--font-sans); font-size:13px; padding:6px; text-align:center}
.text-btn:hover{color:var(--ink)}

/* ===== Result ===== */
/* Result = a soft modal floating over the (blurred) editor, which stays mounted behind it.
   Click the backdrop to return to the editor. Animations are always-on (no reduced-motion gating). */
.result-view{position:fixed; inset:0; z-index:55; align-items:center; justify-content:center; cursor:pointer;
  background:color-mix(in srgb,var(--bg) 55%,transparent);
  -webkit-backdrop-filter:blur(16px) saturate(1.05); backdrop-filter:blur(16px) saturate(1.05);
  animation:resultBackdropIn .26s ease both}
@keyframes resultBackdropIn{from{opacity:0} to{opacity:1}}
.result-card{cursor:default; background:var(--surface); border:1px solid var(--border-muted); border-radius:var(--radius); padding:22px; max-width:min(640px,92vw); display:flex; flex-direction:column; gap:16px; align-items:center; box-shadow:var(--shadow);
  animation:resultCardIn .34s cubic-bezier(.2,.85,.3,1) both}
@keyframes resultCardIn{from{opacity:0; transform:translateY(14px) scale(.965)} to{opacity:1; transform:none}}
#resultImg,#resultVideo{max-width:100%; max-height:60vh; border-radius:var(--radius-sm);
  background:repeating-conic-gradient(var(--checker-a) 0% 25%, var(--checker-b) 0% 50%) 0 / 22px 22px}
#resultVideo{display:block; width:auto}
.result-audio{display:flex; flex-direction:column; align-items:center; gap:14px; width:min(420px,80vw); padding:14px 0 4px}
.result-audio svg{width:64px; height:64px; color:var(--accent)}
.result-audio audio{width:100%}
.wm-remove{display:inline-flex; align-items:center; gap:6px; margin-top:2px}
.wm-remove svg{width:15px; height:15px; opacity:.85}
.result-meta{font-family:var(--font-sans); font-size:12px; color:var(--ink-2)}
/* live meme-caption preview over the stage frame (baked into the export via drawCaption) */
.cap-overlay{position:absolute; inset:0; z-index:5; pointer-events:none; container-type:inline-size;
  display:flex; flex-direction:column; align-items:center; justify-content:flex-start; padding:3.6% 4%;
  font-family:Impact,"Arial Black",var(--font-sans); font-weight:700; text-transform:uppercase; text-align:center; line-height:1.04}
.cap-overlay.bottom{justify-content:flex-end}
.cap-overlay .cap-line{max-width:100%; color:#fff; letter-spacing:.5px; font-size:clamp(13px,8cqw,64px); word-break:break-word;
  text-shadow:-2px -2px 0 #000,2px -2px 0 #000,-2px 2px 0 #000,2px 2px 0 #000,0 0 3px #000}
.cap-input{width:100%; box-sizing:border-box; background:var(--surface-2); border:1px solid var(--border-muted); border-radius:var(--radius-sm);
  color:var(--ink); font-family:var(--font-sans); font-size:13px; padding:8px 10px; margin-bottom:6px}
.cap-input::placeholder{color:var(--ink-3)}
.cap-input:focus{outline:none; border-color:var(--accent)}
.ctl-label.sub{font-size:11.5px; font-weight:500; color:var(--ink-2); letter-spacing:0; text-transform:none; margin-top:9px}
.seg-sm .seg-btn{font-size:11.5px; padding:7px 4px}
/* optional file name (defaults to the source name; blank → "giflab") */
.result-name{display:flex; align-items:center; gap:8px; width:100%; max-width:280px;
  background:var(--surface-2); border:1px solid var(--border-muted); border-radius:var(--radius-sm); padding:8px 12px}
.result-name input{flex:1; min-width:0; background:none; border:0; color:var(--ink); font-family:var(--font-sans); font-size:14px; outline:none}
.rn-clear{flex:none; display:grid; place-items:center; width:20px; height:20px; padding:0; border:0; border-radius:50%;
  background:color-mix(in srgb,var(--ink) 14%,transparent); color:var(--ink-2); cursor:pointer; transition:background .15s, color .15s, transform .12s}
.rn-clear svg{width:12px; height:12px}
.rn-clear:hover{background:color-mix(in srgb,var(--ink) 24%,transparent); color:var(--ink)}
.rn-clear:active{transform:scale(.88)}
.result-name input:placeholder-shown ~ .rn-clear{display:none}   /* empty field → nothing to clear */
.result-name .rn-ext{flex:none; color:var(--ink-3); font-family:var(--font-sans); font-size:14px}
.rn-enter{margin-top:-6px; font-family:var(--font-sans); font-size:11px; color:var(--ink-3); text-align:center; display:flex; align-items:center; gap:6px; justify-content:center}
.rn-enter kbd{font-family:var(--mono); font-size:10px; color:var(--ink-2); background:var(--surface-2); border:1px solid var(--border-muted); border-bottom-width:2px; border-radius:5px; padding:1px 5px}
.result-actions{display:flex; flex-direction:column; gap:8px; width:100%; max-width:300px}
.result-row2{display:flex; gap:8px}
.result-row2 .text-btn{flex:1}
.result-row2 .text-btn:hover{color:var(--ink)}
/* result-screen inline watermark removal + the "evaporate" effect */
.result-stage{position:relative; display:flex; align-items:center; justify-content:center; max-width:100%}
.wm-poof{position:absolute; right:7%; bottom:9%; width:0; height:0; pointer-events:none; z-index:4}
.wm-poof::before{content:''; position:absolute; left:-26px; top:-26px; width:52px; height:52px; border-radius:50%;
  background:radial-gradient(circle, color-mix(in srgb,var(--accent) 62%,transparent) 0%, transparent 68%); opacity:0}
.wm-poof.go::before{animation:poofGlow .8s ease-out forwards}
.wm-poof .spark{position:absolute; left:-3px; top:-3px; width:6px; height:6px; border-radius:50%; background:var(--accent); opacity:0; box-shadow:0 0 6px var(--accent)}
.wm-poof.go .spark{animation:poofSpark .8s ease-out forwards}
@keyframes poofGlow{0%{opacity:.85; transform:scale(.3)} 100%{opacity:0; transform:scale(1.7)}}
@keyframes poofSpark{0%{opacity:1; transform:translate(0,0) scale(1)} 80%{opacity:.9} 100%{opacity:0; transform:translate(var(--dx,0),var(--dy,0)) scale(.3)}}
.rmwm{width:100%; overflow:hidden; max-height:0; opacity:0; transition:max-height .34s ease, opacity .3s ease}
.rmwm.open{max-height:420px; opacity:1}   /* headroom: both steps briefly coexist mid-glide */
.rmwm-text{margin:0 0 8px; font-family:var(--font-sans); font-size:12.5px; line-height:1.45; color:var(--ink-2); text-align:center}
.rmwm-form{display:flex; flex-direction:column; gap:8px}
.rmwm-pending{display:flex; align-items:center; justify-content:center; gap:8px; font-family:var(--font-sans); font-size:12.5px; color:var(--ink-2)}
.mini-spinner{width:14px; height:14px; border-radius:50%; border:2px solid var(--border-muted); border-top-color:var(--accent); animation:spin .7s linear infinite; flex:none}

/* ===== Progress ===== */
.progress{position:fixed; inset:0; background:color-mix(in srgb,var(--bg) 86%,transparent); backdrop-filter:blur(4px); display:grid; place-items:center; z-index:50}
.progress-card{background:var(--surface); border:1px solid var(--border-muted); border-radius:var(--radius); padding:28px 34px; display:flex; flex-direction:column; align-items:center; gap:16px; box-shadow:var(--shadow); width:min(360px,90vw)}
.spinner{width:34px; height:34px; border-radius:50%; border:3px solid var(--surface-2); border-top-color:var(--accent); animation:spin .8s linear infinite}
@keyframes spin{to{transform:rotate(360deg)}}
.progress-label{font-family:var(--font-sans); font-size:13px; color:var(--ink-2)}
.bar{width:100%; height:8px; border-radius:var(--radius-pill); background:var(--surface-2); overflow:hidden; border:1px solid var(--border-muted)}
.bar-fill{height:100%; width:0%; background:var(--accent); transition:width .15s linear}

/* ===== Footer ===== */
/* ===== Watermark control ===== */
.wm-row{display:flex; align-items:center; justify-content:space-between; gap:10px}
.wm-status{font-family:var(--font-sans); font-size:11px; color:var(--ink-2)}
.mini-btn{background:var(--surface-2); color:var(--ink); border:1px solid var(--border-muted); border-radius:var(--radius-pill); padding:5px 12px; font-family:var(--font-sans); font-size:12px; cursor:pointer; transition:border-color .15s, transform .12s}
.mini-btn:hover{border-color:var(--accent)}
.mini-btn:active{transform:scale(.95)}
.mini-btn:disabled{opacity:.55; cursor:not-allowed}
.mini-btn:disabled:hover{border-color:var(--border-muted)}
.mini-btn:disabled:active{transform:none}

/* ===== Rotate control ===== */
/* iPhone-style ruler dial — drag to fine-tune; 90° buttons do the big turns */
.rot-dial{position:relative; height:46px; border-radius:var(--radius-sm); background:var(--surface-2); border:1px solid var(--border-muted);
  overflow:hidden; cursor:ew-resize; touch-action:none; user-select:none; -webkit-user-select:none;
  -webkit-mask-image:linear-gradient(90deg,transparent,#000 14%,#000 86%,transparent);
  mask-image:linear-gradient(90deg,transparent,#000 14%,#000 86%,transparent)}
.rd-track{position:absolute; top:9px; bottom:9px; left:50%; width:1600px; margin-left:-800px; will-change:transform;
  background-image:repeating-linear-gradient(90deg,var(--border-muted-strong) 0 1px,transparent 1px 20px)}
.rd-center{position:absolute; left:50%; top:5px; bottom:5px; width:2px; transform:translateX(-50%);
  background:var(--accent); border-radius:2px; box-shadow:0 0 10px color-mix(in srgb,var(--accent) 70%,transparent)}
.rot-row{display:flex; gap:8px}
.rot-btn{flex:1; display:inline-flex; align-items:center; justify-content:center; gap:6px}
.rot-btn svg{width:15px; height:15px}

/* ===== Modal (watermark email gate) ===== */
.modal{position:fixed; inset:0; background:color-mix(in srgb,var(--bg) 78%,transparent); backdrop-filter:blur(5px); display:grid; place-items:center; z-index:60; padding:20px}
.modal-card{position:relative; background:var(--surface); border:1px solid var(--border-muted); border-radius:var(--radius); padding:28px; width:min(400px,94vw); box-shadow:var(--shadow); display:flex; flex-direction:column; gap:12px}
.modal-x{position:absolute; top:12px; right:12px; width:32px; height:32px; display:grid; place-items:center; background:transparent; border:0; color:var(--ink-2); cursor:pointer; border-radius:var(--radius-pill)}
.modal-x:hover{color:var(--ink); background:var(--surface-2)}
.modal-x svg{width:16px; height:16px}
.modal-title{margin:0; font-size:20px; font-weight:600}
.modal-text{margin:0; color:var(--ink-2); font-size:14px}
.wm-form{display:flex; flex-direction:column; gap:10px; margin-top:4px}
.wm-input{background:var(--surface-2); border:1px solid var(--border-muted); color:var(--ink); border-radius:var(--radius-sm); padding:11px 12px; font-family:var(--font-sans); font-size:15px}
.wm-input:focus{outline:none; border-color:var(--accent)}
.wm-err{margin:2px 0 0; font-family:var(--font-sans); font-size:12px; color:#ff6b6b}
/* code-confirmation step */
.wm-code{text-align:center; letter-spacing:0.42em; padding-left:0.42em; font-family:'Chivo Mono',ui-monospace,monospace; font-size:22px; font-weight:600}
.wm-resend{margin:9px 0 0; font-family:var(--font-sans); font-size:12px; color:var(--ink-3)}
.link-btn{background:none; border:0; padding:0; color:var(--accent); font:inherit; cursor:pointer; text-decoration:underline}
.link-btn:disabled{opacity:.6; cursor:default; text-decoration:none}
.modal-fine{margin:0; font-family:var(--font-sans); font-size:10px; line-height:1.5; color:var(--ink-3)}
textarea.fb-area{min-height:104px; resize:vertical; line-height:1.5; font-size:14px}
html.desktop-app .foot-feedback{display:none}   /* feedback is web-only — the desktop app has its own channel */

/* Canon fix (LEGAL) + no-jump load: the free-web-tool page MUST expose Terms/Acceptable-Use footer links
   without a click-wall, AND loading a clip must NOT switch layout modes (the old app-shell lock made the
   preview "fly" on load). So on the WEB (not the packaged desktop) the WHOLE editor — empty-drop AND loaded
   — stays a normal scrollable page with the footer shown; the SITE layer (giflab-site.css) pins the preview
   sticky across both states so it stays put. Scoped to body.editing (both states); paired with the site
   layer's matching body.editing scope — deploy the two ATOMICALLY (see scope-no-whole-site memory). Additive
   high-specificity override; base app-shell + desktop untouched. (The result modal is position:fixed inset:0
   → it still fully covers the scrollable page behind it.) */
html:not(.desktop-app) body.editing{height:auto; min-height:100vh; overflow:visible}
html:not(.desktop-app) body.editing .stage{overflow:visible; min-height:0}
html:not(.desktop-app) body.editing .edit-view{min-height:0}
html:not(.desktop-app) body.editing .editor{height:auto; min-height:0; grid-template-rows:auto; align-items:start}
html:not(.desktop-app) body.editing .controls{max-height:none; min-height:0; overflow:visible; padding-right:0}
html:not(.desktop-app) body.editing .preview-col{align-self:start}
html:not(.desktop-app) body.editing .site-foot{display:block}
/* Desktop-width landing-scroll (controls static + preview sticky-pinned, "колёсиком двигаются только левые
   тулзы, превью закреплено") is owned by the SITE layer (giflab-site.css, loaded after this file → single
   owner + one true rest offset). Do NOT re-add a .controls/.preview-col @media block here: a copy here
   duplicated it with a stale top and reintroduced the Easy/Advanced preview jump. See scope-no-whole-site. */

/* ===== Site footer (copied from ctlab.app assets/styles.css; re-sync on site chrome changes) ===== */
.site-foot{border-top:1px solid var(--border); margin-top:50px; padding:48px 40px 30px; position:relative; z-index:1}
/* crawlable SEO copy — visible but modest (the page's single H1 sits here, tool has no hero) */
.foot-seo{max-width:720px; margin:0 auto 36px; text-align:center}
.foot-seo h1{margin:0 0 8px; font-family:var(--font-sans); font-size:15px; font-weight:600; letter-spacing:0; color:var(--text2)}
.foot-seo p{margin:0; font-family:var(--font-sans); font-size:12.5px; line-height:1.7; color:var(--text3)}
.foot-grid{max-width:1060px; margin:0 auto; display:grid; grid-template-columns:1.7fr 1fr 1fr 1fr 1fr; gap:30px}
.foot-brand .fb-logo{display:flex; align-items:center; margin-bottom:14px}
.foot-brand .logo-wm{height:30px; width:auto; display:block; color:var(--text)}
.foot-brand p{font-size:12px; color:var(--text2); line-height:1.65; max-width:250px}
.foot-col h4{font-size:11px; font-family:var(--mono); text-transform:uppercase; letter-spacing:1px; color:var(--text3); margin-bottom:14px; font-weight:400}
.foot-col a{display:block; font-size:13px; color:var(--text2); margin-bottom:9px; transition:color .2s; text-decoration:none}
.foot-col a:hover{color:var(--text)}
.foot-bottom{max-width:1060px; margin:30px auto 0; padding-top:20px; border-top:1px solid var(--border); display:flex; justify-content:flex-end; gap:14px; font-size:11px; font-family:var(--mono); color:var(--text3)}
@media(min-width:1100px){
  .foot-bottom{position:absolute; top:48px; right:40px; margin:0; max-width:none; padding-top:0; border-top:none}
}

/* ===== Responsive: editor (tool) ===== */
@media (max-width:980px){
  /* flex column (not grid) so the sticky preview is bounded by the whole editor, not its grid cell.
     align-items:stretch overrides the grid base's align-items:start — without it flex items size to
     their content (the preview shrinks to the video width and overflows narrow viewports). */
  .editor{display:flex; flex-direction:column; gap:16px; align-items:stretch; touch-action:pan-y}
  .preview-col,.controls{min-width:0}
  /* pin the player just under the fixed header (app-shell block below); the tooling scrolls under it */
  .preview-col{order:0; position:sticky; top:54px; z-index:6; padding:8px 0 12px;
    background:var(--bg); box-shadow:0 14px 22px -12px rgba(0,0,0,.8)}
  .under-stage{display:none}                  /* touch: no keyboard shortcuts + tools are gestures, not buttons */
  .view-bar{display:none}                      /* touch: pinch already zooms; keep the pinned preview clean */
  .stage-tip{display:none}                     /* touch: no drag-and-drop from disk */
  .stage-name{display:none}                    /* touch: keep the pinned preview clean */
  .tool-palette{display:none}                 /* tools are done with finger gestures, not buttons */
  .gesture-hint{display:block; text-align:center; font-family:var(--font-sans); font-size:11px; color:var(--ink-3); padding:2px 8px 0}
  .controls{position:static; order:2}
}
/* ===== Touch-friendly controls (bigger tap targets on phones/tablets) ===== */
@media (max-width:980px){
  /* fingers manipulate the preview (pan/pinch/rotate) instead of scrolling/zooming the page */
  .stage-wrap{touch-action:none}
  .seg{padding:6px}
  .seg-btn{padding:11px 8px; font-size:14px}
  .range{height:8px}
  .range::-webkit-slider-thumb{width:26px; height:26px; border-width:4px}
  .range::-moz-range-thumb{width:24px; height:24px; border-width:4px}
  .timeline{height:32px}                        /* taller strip for touch (flags poke above) */
  .tl-handle{top:-24px; width:30px; height:22px} /* bigger flag grab, in the lane above the strip */
  .tl-handle::before{width:20px; height:20px; border-radius:5px}
  .tl-handle::after{top:12px; height:44px}
  .tl-playhead{top:-24px; height:calc(100% + 24px)}
  .tool-btn{width:42px; height:42px}
  .tool-btn svg{width:21px; height:21px}
  .mini-btn{padding:10px 14px; font-size:13px}
  .align-grid{width:128px}
  .num{padding:10px; font-size:14px}
  .zoom-field input{width:54px; padding:6px 7px; font-size:14px}
  .frame-color input[type=color]{width:46px; height:32px}
  .check input{width:22px; height:22px}
  .ghost-btn{width:48px; height:48px}
  .ghost-btn svg{width:20px; height:20px}
  .close-btn{width:40px; height:40px}
  .close-btn svg{width:18px; height:18px}
  .primary-btn{padding:15px 16px; font-size:16px}
  .text-btn{padding:10px; font-size:14px}
}

/* ===== Responsive: site chrome (nav + footer), copied from ctlab.app ===== */
@media (max-width:768px){
  nav{padding:14px 20px}
  .logo-wm{height:38px}
  .nav-toggle{display:flex}
  .nav-links{
    position:absolute; top:100%; left:0; right:0; flex-direction:column; align-items:stretch; gap:0;
    background:rgba(10,10,10,0.98); backdrop-filter:blur(12px); -webkit-backdrop-filter:blur(12px);
    border-bottom:1px solid var(--border); padding:8px 20px 16px;
    transform:translateY(-12px); opacity:0; pointer-events:none;
    transition:opacity .25s var(--ease),transform .25s var(--ease);
  }
  .nav-links.open{transform:none; opacity:1; pointer-events:auto}
  .nav-links a{padding:12px 0; font-size:15px; border-bottom:1px solid var(--border)}
  .nav-links a:not(.nav-cta)::after{display:none}
  .nav-links a:last-child{border-bottom:none}
  .nav-cta{text-align:center; margin-top:12px; padding:12px 16px!important; font-size:14px; border-radius:9px}
  .stage{padding:24px 20px 40px}
  .foot-grid{grid-template-columns:1fr 1fr; gap:26px}
  .foot-brand{grid-column:1/-1}
  .site-foot{padding:38px 20px 24px}
  .foot-bottom{justify-content:flex-start}
}

/* ===== Mobile app shell: fixed header + pinned preview that never moves; only controls scroll ===== */
@media (max-width:980px){
  nav{position:fixed; top:0; left:0; right:0; height:54px; padding:0 18px; background:var(--bg); z-index:100}
  .logo-wm{height:30px}
  body{padding-top:54px}        /* clear the fixed header */
  .stage{padding-top:0}
  .preview-col{top:54px}
  .stage-wrap{height:44vh; height:44svh}   /* stable preview height (svh → immune to the URL bar) */
  /* edit view = a NON-scrolling app shell: the page itself never scrolls, only the controls do. So iOS
     never shows/hides the URL bar mid-scroll → the preview never resizes or jumps. */
  body.editing{height:100vh; height:100svh; overflow:hidden}
  body.editing .stage{min-height:0; overflow:hidden; padding-bottom:0}
  body.editing .edit-view{min-height:0}
  body.editing .editor{flex:1; min-height:0}
  body.editing .preview-col{position:static; flex:none}
  body.editing .controls{flex:1; min-height:0; overflow:hidden auto; -webkit-overflow-scrolling:touch; padding-bottom:24px}
  body.editing .site-foot{display:none}   /* footer/legal stays available on the drop & result views */
}

/* ===== Desktop app shell: editor fills the viewport; the side panels (tools rail + controls) scroll
   INTERNALLY so they stay put instead of riding the page scroll (sticky can't pin them — the tallest
   panel sizes the grid row, leaving no slack to stick against). ===== */
@media (min-width:981px){
  /* App-style fixed-height shell: the preview + timeline stay PINNED on the right while the CONTROLS
     (tools) panel on the left scrolls INTERNALLY — same UX as the desktop app. The Export button lives
     at the bottom of that scrolling panel, so it's always reachable by scrolling the tools. */
  body.editing{height:100vh; overflow:hidden}
  body.editing .stage{min-height:0; overflow:hidden}
  body.editing .view{min-height:0}
  body.editing .editor{height:100%; min-height:0; grid-template-rows:minmax(0,1fr); align-items:stretch}
  /* Real right gap (>= scrollbar width) so the bar never overlaps the controls. macOS overlay scrollbars
     reserve ZERO layout space, so scrollbar-gutter:stable is a no-op there — a padding gap works for both. */
  /* overflow-y:SCROLL (not auto) so the scrollbar's space is ALWAYS reserved — the controls column stays
     the SAME width whether the content scrolls (GIF, taller) or fits (MP4/MP3). Track is transparent, so
     the reserved bar is invisible when there's nothing to scroll. */
  body.editing .controls{position:static; top:auto; max-height:100%; min-height:0;
    overflow:hidden scroll; scrollbar-width:thin; padding-right:14px}
  body.editing .preview-col{align-self:start; min-height:0}
  body.editing .site-foot{display:none}
  body.editing .stage-wrap{height:clamp(220px,46vh,460px)}   /* keep the pinned preview within the window */
  body.editing .tool-legend{display:none}   /* Move/Rotate/Scale cards are redundant with the shortcuts row + on-canvas tools */
}

/* ===== Packaged desktop app (?app=1) — it's a standalone app, not a web page. Drop the website nav
   (the OS title bar is the chrome) and compact the editor so it fits the window without scrolling. ===== */
.desktop-app nav{display:none}
.desktop-app .site-foot{display:none}
.desktop-app .stage{padding-top:16px}
@media (min-width:981px){
  .desktop-app .stage{padding:12px 36px 14px}
  .desktop-app .editor{gap:18px}
  .desktop-app .controls{gap:14px}            /* a touch tighter than the web's 26px */
  .desktop-app .tool-legend{display:none}     /* tool cards are redundant with the left tool rail */
  /* slightly smaller preview so the keyboard-shortcuts caption fits under it in the pinned column */
  .desktop-app .stage-wrap{height:clamp(220px,48vh,460px)}
  /* Preview + timeline stay PINNED; the CONTROLS panel scrolls INTERNALLY. TOP-ALIGNED with a comfortable,
     window-scaled top offset (padding-top) so the editor sits a bit down from the top instead of jammed
     against it — and the offset is FIXED per window, so switching Easy↔Advanced keeps the top (and the
     preview) exactly put and just grows the tools DOWNWARD (they scroll internally if they outgrow the
     window). NOT centred: centring shifted the whole block + the preview up whenever the tools got tall. */
  .desktop-app body.editing .stage{overflow:hidden; padding-top:clamp(24px,13vh,200px)}
  .desktop-app body.editing .editor{height:100%; min-height:0; grid-template-rows:minmax(0,1fr); align-items:stretch}
  .desktop-app body.editing .controls{max-height:100%; min-height:0; overflow:hidden scroll; scrollbar-width:thin; padding-right:14px}
  .desktop-app body.editing .preview-col{align-self:start; min-height:0}
}
