/* ==========================================================================
   QuoteStitch — cinematic art direction
   Palette: projection-room dark, warm projector gold, film grain.
   ========================================================================== */

:root{
  /* surfaces */
  --void:#07070a;
  --film:#0c0c11;
  --film-2:#13131a;
  --film-3:#1b1b24;
  --edge:rgba(255,255,255,.07);
  --edge-2:rgba(255,255,255,.14);
  --edge-3:rgba(255,255,255,.22);

  /* ink */
  --ink:#f5f2ec;
  --ink-2:#a9a49a;
  --ink-3:#6c675e;

  /* light */
  --gold:#e5b567;
  --gold-2:#f2d29a;
  --gold-dim:rgba(229,181,103,.28);
  --ember:#d4553f;

  /* type — Latin webfonts first, CJK system fonts as fallback so mixed
     English/Chinese copy renders each script with the right face. */
  --cjk-serif:"Songti SC","Source Han Serif SC","Noto Serif CJK SC","STSong","SimSun",serif;
  --cjk-sans:"PingFang SC","HarmonyOS Sans SC","Source Han Sans SC","Noto Sans CJK SC","Microsoft YaHei",sans-serif;
  --serif:"Instrument Serif","Songti SC","Source Han Serif SC","Noto Serif CJK SC","STSong",ui-serif,Georgia,serif;
  --sans:"Inter","PingFang SC","HarmonyOS Sans SC","Source Han Sans SC","Noto Sans CJK SC","Microsoft YaHei",-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,sans-serif;
  --mono:"JetBrains Mono","PingFang SC","Microsoft YaHei",ui-monospace,SFMono-Regular,Menlo,monospace;

  /* motion */
  --ease:cubic-bezier(.22,1,.36,1);
  --ease-soft:cubic-bezier(.4,0,.2,1);

  --radius:14px;
  --radius-sm:9px;
}

*{box-sizing:border-box}
/* author `display` beats the UA rule for [hidden]; without this the
   workspace shows through on first load and behind the video panel */
[hidden]{display:none !important}
html{-webkit-text-size-adjust:100%;scroll-behavior:smooth}
body{
  margin:0;
  background:var(--void);
  color:var(--ink);
  font-family:var(--sans);
  font-size:16px;
  line-height:1.65;
  -webkit-font-smoothing:antialiased;
  text-rendering:optimizeLegibility;
  overflow-x:hidden;
}
.wrap{max-width:1140px;margin:0 auto;padding:0 28px}
a{color:var(--gold)}
h1,h2,h3{font-weight:400;line-height:1.15;margin:0}
em{font-style:italic}
::selection{background:var(--gold-dim);color:var(--ink)}

/* ---------------------------------------------------------- atmosphere */

.grain,.vignette,.beam{position:fixed;pointer-events:none;z-index:1}
.grain{
  inset:-50%;
  opacity:.05;
  background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='180' height='180'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='.85' numOctaves='3'/%3E%3C/filter%3E%3Crect width='180' height='180' filter='url(%23n)'/%3E%3C/svg%3E");
  animation:grain 1.2s steps(4) infinite;
}
@keyframes grain{
  0%{transform:translate(0,0)}
  25%{transform:translate(-3%,2%)}
  50%{transform:translate(2%,-3%)}
  75%{transform:translate(-2%,-2%)}
  100%{transform:translate(0,0)}
}
.vignette{
  inset:0;
  background:radial-gradient(120% 80% at 50% 0%,transparent 40%,rgba(0,0,0,.55) 100%);
  z-index:0;
}
.beam{
  top:-340px;left:50%;width:900px;height:700px;transform:translateX(-50%);
  background:radial-gradient(closest-side,rgba(229,181,103,.16),transparent 72%);
  z-index:0;
}
.site-head,.hero,.tool,.content,.site-foot{position:relative;z-index:2}

/* ------------------------------------------------------------ reveal */

.reveal{
  opacity:0;transform:translateY(28px);filter:blur(7px);
  transition:opacity .95s var(--ease),transform .95s var(--ease),filter .95s var(--ease);
  transition-delay:var(--d,0ms);
}
.reveal.in{opacity:1;transform:none;filter:none}
@media (prefers-reduced-motion:reduce){
  .reveal{opacity:1;transform:none;filter:none;transition:none}
  .grain{animation:none}
  html{scroll-behavior:auto}
}

/* ------------------------------------------------------------- header */

.site-head{
  position:sticky;top:0;z-index:30;
  background:rgba(7,7,10,.72);
  backdrop-filter:saturate(1.3) blur(14px);
  border-bottom:1px solid var(--edge);
}
.head-inner{display:flex;align-items:center;justify-content:space-between;height:66px}
.brand{display:flex;align-items:center;gap:11px;padding:10px 4px;margin:-10px -4px;text-decoration:none;color:var(--ink)}
.brand-mark{
  width:24px;height:24px;border-radius:6px;background:var(--film-3);
  border:1px solid var(--edge-2);position:relative;flex:0 0 auto;
  box-shadow:inset 0 0 12px rgba(229,181,103,.14);
  transition:transform .5s var(--ease),box-shadow .5s var(--ease);
}
.brand:hover .brand-mark,.brand:focus-visible .brand-mark{
  transform:translateY(-1px) rotate(-2.5deg);
  box-shadow:inset 0 0 12px rgba(229,181,103,.34),0 0 20px -8px rgba(229,181,103,.7);
}
.brand-mark::before,.brand-mark::after{content:"";position:absolute;left:5px;right:5px;border-radius:2px}
.brand-mark::before{top:5px;height:7px;background:var(--gold)}
.brand-mark::after{top:15px;height:4px;background:var(--gold);opacity:.45}
.brand-mark.small{width:18px;height:18px}
.brand-mark.small::before{top:4px;left:4px;right:4px;height:5px}
.brand-mark.small::after{top:11px;left:4px;right:4px;height:3px}
.brand-name{font-weight:500;font-size:15px;letter-spacing:.02em}
.head-nav{display:flex;gap:18px}
.head-nav a{
  position:relative;display:inline-block;padding:12px 4px;
  color:var(--ink-2);text-decoration:none;font-size:12.5px;letter-spacing:.1em;
  text-transform:uppercase;transition:color .3s var(--ease-soft);
}
/* the rule grows from the left instead of just switching colour */
.head-nav a::after{
  content:"";position:absolute;left:4px;right:4px;bottom:8px;height:1px;
  background:currentColor;transform:scaleX(0);transform-origin:left center;
  transition:transform .42s var(--ease);
}
.head-nav a:hover,.head-nav a:focus-visible{color:var(--gold)}
.head-nav a:hover::after,.head-nav a:focus-visible::after{transform:scaleX(1)}
.head-right{display:flex;align-items:center;gap:22px}

/* language switch — two real links, one per language page, so search
   engines can follow them instead of relying on JS */
.lang-switch{
  display:flex;align-items:center;gap:2px;flex:0 0 auto;
  border:1px solid var(--edge-2);border-radius:999px;padding:3px;
  background:rgba(255,255,255,.03);
}
.lang-switch a,.lang-switch .lang-on{
  font-size:11.5px;letter-spacing:.06em;text-decoration:none;line-height:1.35;
  padding:4px 11px;border-radius:999px;white-space:nowrap;
  transition:color .3s var(--ease-soft),background .3s var(--ease-soft);
}
.lang-switch a{color:var(--ink-2)}
.lang-switch a:hover{color:var(--gold)}
.lang-switch .lang-on{background:var(--gold);color:var(--void);font-weight:500}

/* --------------------------------------------------------------- hero */

.hero{padding:62px 0 30px}

/* Two columns on desktop: the copy sits beside the input, so the tool is
   reachable on arrival instead of sitting below the fold. Once the tool is
   in use the copy column steps aside and the input takes the full width. */
.hero-grid{
  display:grid;
  grid-template-columns:minmax(0,1.08fr) minmax(330px,.92fr);
  gap:44px;align-items:center;
}
.hero-copy,.hero-tool{min-width:0}
.hero:has(#dropzone[hidden]):has(#videoRoot[hidden]){display:none}
.hero:has(#videoRoot:not([hidden])) .hero-grid{grid-template-columns:minmax(0,1fr)}
.hero:has(#videoRoot:not([hidden])) .hero-copy{display:none}
.eyebrow{
  margin:0 0 18px;font-size:11px;font-weight:500;letter-spacing:.24em;
  text-transform:uppercase;color:var(--gold);
}
.eyebrow::before{
  content:"";display:inline-block;width:26px;height:1px;background:var(--gold-dim);
  vertical-align:middle;margin-right:12px;
}
.hero-title{
  font-family:var(--serif);
  font-size:clamp(2.25rem,4.4vw,4.05rem);
  line-height:.98;letter-spacing:-.022em;margin:0;
}
.hero-title .line{display:block}
.hero-title em{color:var(--gold);font-style:italic}
.lede strong{color:var(--ink);font-weight:500}
.lede{
  margin:24px 0 0;max-width:620px;color:var(--ink-2);
  font-size:clamp(15px,1.5vw,17px);line-height:1.68;
}
.badges{display:flex;flex-wrap:wrap;gap:8px;margin:26px 0 0;padding:0;list-style:none}
.badges span{
  font-size:11.5px;letter-spacing:.03em;color:var(--ink-2);
  background:rgba(255,255,255,.03);border:1px solid var(--edge);
  border-radius:999px;padding:5px 12px;white-space:nowrap;
}

/* --------------------------------------------------------- tool frame */

.tool{padding:30px 28px 8px}
/* the output frame only exists once there is something to output */
.tool:has(#workspace[hidden]){display:none}
.tool-frame{
  position:relative;border:1px solid var(--edge);border-radius:var(--radius);
  background:linear-gradient(180deg,rgba(255,255,255,.022),rgba(255,255,255,0) 42%),var(--film);
  padding:26px;
  box-shadow:0 40px 90px -50px rgba(0,0,0,.95),0 0 0 1px rgba(0,0,0,.4);
}
.frame-marks i{position:absolute;width:15px;height:15px;border:1px solid rgba(229,181,103,.45)}
.frame-marks i:nth-child(1){top:12px;left:12px;border-right:0;border-bottom:0;border-radius:4px 0 0 0}
.frame-marks i:nth-child(2){top:12px;right:12px;border-left:0;border-bottom:0;border-radius:0 4px 0 0}
.frame-marks i:nth-child(3){bottom:12px;left:12px;border-right:0;border-top:0;border-radius:0 0 0 4px}
.frame-marks i:nth-child(4){bottom:12px;right:12px;border-left:0;border-top:0;border-radius:0 0 4px 0}

/* dropzone */
.dropzone{
  border:1px dashed var(--edge-2);border-radius:var(--radius-sm);
  padding:44px 26px;text-align:center;cursor:pointer;
  transition:border-color .45s var(--ease),background .45s var(--ease),transform .45s var(--ease);
  background:radial-gradient(90% 130% at 50% 0%,rgba(229,181,103,.05),transparent 70%);
}
.dropzone:hover,.dropzone.is-over{
  border-color:var(--gold);
  background:radial-gradient(90% 130% at 50% 0%,rgba(229,181,103,.13),transparent 70%);
}
/* keyboard focus gets its own ring — never `outline:none` here, or the most
   important target on the page becomes the only one with no focus indicator */
.dropzone:focus-visible{
  border-color:var(--gold);outline:2px solid var(--gold);outline-offset:4px;
  background:radial-gradient(90% 130% at 50% 0%,rgba(229,181,103,.13),transparent 70%);
}
/* A drag that has entered the target will copy the files in, not move them.
   The native "copy" cursor says exactly that, where a plain pointer says
   nothing — and it only appears mid-drag, so the idle affordance stays a
   pointer and the target still reads as clickable. */
.dropzone.is-over{cursor:copy;transform:scale(1.008)}

/* ------------------------------------------------------- hero demo
   Four shots on a contact sheet converge into one stitched image.
   All geometry is a percentage of a fixed-ratio box, so it scales with
   the column instead of needing a breakpoint. */

.demo{
  position:relative;width:100%;max-width:430px;margin:0 auto 20px;
  aspect-ratio:430 / 190;
  transition:filter .45s var(--ease);
}
.dropzone:hover .demo,.dropzone:focus-visible .demo{filter:brightness(1.14)}
.demo span{position:absolute;display:block;box-sizing:border-box}

.dscene{
  left:var(--x);top:var(--y);width:44.5%;height:42%;
  border-radius:4px;border:1px solid var(--edge);
  background:
    linear-gradient(180deg,rgba(255,255,255,.075),rgba(255,255,255,.015) 55%),
    var(--film-3);
  animation:dScene 7.4s var(--ease-soft) infinite;
  animation-delay:calc(var(--i) * -.1s);
}
/* two content lines so a frame reads as a screenshot, not a plain box */
.dscene::before,.dscene::after{
  content:"";position:absolute;left:7%;height:3px;border-radius:2px;
  background:rgba(255,255,255,.16);
}
.dscene::before{top:14%;width:40%}
.dscene::after{top:32%;width:24%}

.dband{
  left:var(--x);top:var(--by);width:44.5%;height:7.4%;
  border-radius:2px;
  background:linear-gradient(180deg,rgba(229,181,103,.1),rgba(229,181,103,.04));
  border:1px solid rgba(229,181,103,.3);
  display:flex;align-items:center;justify-content:center;gap:5%;
  animation:dBand 7.4s var(--ease-soft) infinite;
  animation-delay:calc(var(--i) * -.1s);
}
.dband b{
  display:block;height:2px;border-radius:2px;
  background:var(--gold-2);box-shadow:0 0 7px rgba(229,181,103,.55);
}

.dflash{
  left:27.75%;top:3%;width:44.5%;height:67.5%;
  border-radius:4px;pointer-events:none;opacity:0;filter:blur(8px);
  background:linear-gradient(180deg,rgba(229,181,103,.55),rgba(229,181,103,0));
  animation:dFlash 7.4s var(--ease-soft) infinite;
}

/* 7.4s loop: 0.7s rest → 1.7s transform → 4.1s hold on the result → 0.9s back.
   The long hold matters: the resting state is the product's value, and it
   means a paused or half-noticed demo still shows the finished strip. */
@keyframes dScene{
  0%,9%   {left:var(--x);top:var(--y);opacity:1}
  32%,88% {left:27.75%;top:3%;opacity:var(--o)}
  100%    {left:var(--x);top:var(--y);opacity:1}
}
@keyframes dBand{
  0%,9%   {left:var(--x);top:var(--by)}
  32%,88% {left:27.75%;top:var(--oy)}
  100%    {left:var(--x);top:var(--by)}
}
@keyframes dFlash{
  0%,26%   {opacity:0}
  35%      {opacity:.8}
  50%,100% {opacity:0}
}

/* hovering or focusing the dropzone freezes the loop — same behaviour the
   old static glyph had, so a visitor can stop it on a frame they want */
.dropzone:hover .demo span,
.dropzone:focus-visible .demo span,
.dropzone.is-over .demo span{animation-play-state:paused}

/* No loop for reduced-motion — hold the finished long image instead. This
   block has to sit after the demo rules above, or the base `left`/`top`
   declarations win on source order and the demo freezes on the input state. */
@media (prefers-reduced-motion:reduce){
  .demo span{animation:none}
  .dscene{left:27.75%;top:3%;opacity:var(--o)}
  .dband{left:27.75%;top:var(--oy)}
  .demo .dflash{display:none}
}
.dz-title{margin:0;font-family:var(--serif);font-size:24px;letter-spacing:-.01em}
.dz-sub{margin:10px 0 0;color:var(--ink-2);font-size:13.5px}
.dz-hint{margin:16px 0 0;color:var(--ink-3);font-size:11px;letter-spacing:.08em;text-transform:uppercase}
.link{color:var(--gold);text-decoration:underline;text-underline-offset:3px}
kbd{
  font-family:var(--mono);font-size:11px;background:var(--film-3);color:var(--ink-2);
  border:1px solid var(--edge-2);border-radius:5px;padding:2px 6px;
}

/* ----------------------------------------------------------- workspace */

.workspace{display:grid;grid-template-columns:296px minmax(0,1fr);gap:20px;align-items:start}
.panel{
  background:var(--film-2);border:1px solid var(--edge);border-radius:var(--radius-sm);
  padding:4px 18px 18px;
}
.panel-block{padding:16px 0;border-bottom:1px solid var(--edge)}
.panel-block:last-child{border-bottom:0}
.panel-title{
  font-size:10.5px;text-transform:uppercase;letter-spacing:.18em;
  color:var(--ink-3);font-weight:500;margin:0 0 12px;
  display:flex;align-items:center;gap:8px;
}
.count{
  background:var(--gold-dim);color:var(--gold-2);border-radius:999px;
  padding:1px 8px;font-size:10.5px;letter-spacing:0;font-family:var(--mono);
}
.seg{display:flex;background:var(--void);border-radius:var(--radius-sm);padding:3px;gap:3px;border:1px solid var(--edge)}
.seg-btn{
  flex:1;border:0;background:transparent;padding:8px;border-radius:6px;
  font:inherit;font-size:12.5px;color:var(--ink-2);cursor:pointer;
  transition:color .3s var(--ease-soft),background .3s var(--ease-soft);
}
.seg-btn:hover{color:var(--ink)}
.seg-btn.is-on{background:var(--film-3);color:var(--gold-2);font-weight:500}
.note{margin:11px 0 0;font-size:11.5px;color:var(--ink-3);line-height:1.55}

.row{display:flex;align-items:center;gap:11px;margin:11px 0;font-size:12.5px;color:var(--ink-2)}
.row>span:first-child{flex:0 0 72px;letter-spacing:.02em}
.row output{flex:0 0 46px;text-align:right;font-family:var(--mono);font-size:11px;color:var(--gold)}
.row select{
  flex:1;min-width:0;font:inherit;font-size:12.5px;padding:6px 26px 6px 9px;
  border:1px solid var(--edge-2);border-radius:6px;background-color:var(--void);color:var(--ink);
  appearance:none;
  background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='10' height='6'%3E%3Cpath d='M1 1l4 4 4-4' fill='none' stroke='%236c675e' stroke-width='1.4'/%3E%3C/svg%3E");
  background-repeat:no-repeat;background-position:right 10px center;
}
.row input[type=color]{
  width:44px;height:28px;padding:2px;border:1px solid var(--edge-2);
  border-radius:6px;background:var(--void);cursor:pointer;
}
.row input[type=range]{
  flex:1;min-width:0;-webkit-appearance:none;appearance:none;height:22px;background:transparent;cursor:pointer;
}
.row input[type=range]::-webkit-slider-runnable-track{height:2px;background:var(--edge-3);border-radius:2px}
.row input[type=range]::-webkit-slider-thumb{
  -webkit-appearance:none;width:13px;height:13px;border-radius:50%;
  background:var(--gold);margin-top:-5.5px;border:0;
  box-shadow:0 0 0 4px rgba(229,181,103,.14);
  transition:box-shadow .3s var(--ease-soft);
}
.row input[type=range]:hover::-webkit-slider-thumb{box-shadow:0 0 0 7px rgba(229,181,103,.2)}
.row input[type=range]::-moz-range-track{height:2px;background:var(--edge-3);border-radius:2px}
.row input[type=range]::-moz-range-thumb{width:13px;height:13px;border:0;border-radius:50%;background:var(--gold)}

.btn-ghost{
  width:100%;margin-top:12px;padding:9px;border:1px solid var(--edge-2);
  background:transparent;border-radius:var(--radius-sm);font:inherit;font-size:12.5px;
  color:var(--ink-2);cursor:pointer;
  transition:border-color .35s var(--ease-soft),color .35s var(--ease-soft),background .35s var(--ease-soft);
}
.btn-ghost:hover{border-color:var(--gold);color:var(--gold-2);background:rgba(229,181,103,.05)}
.btn-ghost.danger:hover{border-color:var(--ember);color:#e88b78;background:rgba(212,85,63,.08)}
:focus-visible{outline:2px solid var(--gold);outline-offset:3px}

/* Global busy state, set while a video is being analysed or extracted. The
   `*` + !important is load-bearing: every control below sets its own cursor
   (pointer / grab / not-allowed), and those would otherwise show through and
   contradict the progress cursor on whatever the pointer happens to rest on. */
body.is-working,body.is-working *{cursor:progress !important}

.thumbs{list-style:none;margin:0;padding:0;display:flex;flex-direction:column;gap:6px;max-height:286px;overflow:auto}
.thumb{
  display:flex;align-items:center;gap:10px;padding:7px;border-radius:7px;
  background:var(--void);border:1px solid transparent;cursor:grab;
  transition:border-color .3s var(--ease-soft),opacity .3s var(--ease-soft);
}
.thumb:hover{border-color:var(--edge-2)}
.thumb.is-drag{opacity:.35}
.thumb.is-over{border-color:var(--gold);border-style:dashed}
.thumb img{width:54px;height:34px;object-fit:cover;border-radius:4px;flex:0 0 auto;background:var(--film-3)}
.thumb-meta{min-width:0;flex:1}
.thumb-name{font-size:12px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;color:var(--ink)}
.thumb-sub{font-size:10.5px;color:var(--ink-3);font-family:var(--mono)}
.thumb-x{
  border:0;background:transparent;color:var(--ink-3);cursor:pointer;font-size:16px;
  line-height:1;padding:4px 7px;border-radius:5px;flex:0 0 auto;
  transition:color .25s var(--ease-soft),background .25s var(--ease-soft);
}
.thumb-x:hover{color:#e88b78;background:rgba(212,85,63,.12)}
.thumb-actions{display:flex;gap:8px}
.thumb-actions .btn-ghost{margin-top:10px}

/* stage */
.stage{background:var(--film-2);border:1px solid var(--edge);border-radius:var(--radius-sm);overflow:hidden}
.stage-bar{
  display:flex;align-items:center;justify-content:space-between;gap:14px;
  padding:12px 16px;border-bottom:1px solid var(--edge);
}
.stage-info{font-size:11.5px;color:var(--ink-3);font-family:var(--mono);letter-spacing:.01em}
.btn-primary{
  position:relative;overflow:hidden;border:0;cursor:pointer;
  background:linear-gradient(180deg,var(--gold-2),var(--gold));
  color:#241a06;font:inherit;font-size:12.5px;font-weight:600;letter-spacing:.02em;
  padding:10px 20px;border-radius:8px;
  box-shadow:0 8px 26px -12px rgba(229,181,103,.7);
  transition:transform .35s var(--ease),box-shadow .35s var(--ease),opacity .3s linear;
}
.btn-primary span{position:relative;z-index:2}
.btn-primary::after{
  content:"";position:absolute;inset:0;z-index:1;
  background:linear-gradient(105deg,transparent 32%,rgba(255,255,255,.55) 50%,transparent 68%);
  transform:translateX(-130%);
  transition:transform .75s var(--ease);
}
.btn-primary:hover{transform:translateY(-1px);box-shadow:0 12px 32px -12px rgba(229,181,103,.85)}
.btn-primary:hover::after{transform:translateX(130%)}
.btn-primary:disabled{opacity:.35;cursor:not-allowed;transform:none;box-shadow:none}
.preview{
  background:radial-gradient(90% 70% at 50% 0%,rgba(229,181,103,.055),transparent 62%),var(--void);
  max-height:70vh;overflow:auto;padding:22px;text-align:center;
  scrollbar-width:thin;scrollbar-color:var(--edge-2) transparent;
}
.preview canvas{
  max-width:100%;height:auto;background:#fff;vertical-align:top;border-radius:3px;
  box-shadow:0 24px 70px -24px rgba(0,0,0,.95),0 0 0 1px rgba(255,255,255,.08);
}
.stage-foot{margin:0;padding:11px 16px;border-top:1px solid var(--edge);font-size:11.5px;color:var(--ink-3)}
.preview::-webkit-scrollbar{width:9px}
.preview::-webkit-scrollbar-thumb{background:var(--edge-2);border-radius:9px}
.preview::-webkit-scrollbar-track{background:transparent}

.error{
  margin:18px 0 0;padding:14px 16px;border-radius:var(--radius-sm);
  background:rgba(212,85,63,.1);border:1px solid rgba(212,85,63,.35);
  color:#eda593;font-size:13.5px;
}

/* ------------------------------------------------------------- content */

.content{padding:118px 28px 0}
.content h2{
  font-family:var(--serif);font-size:clamp(2rem,4.4vw,3.4rem);
  letter-spacing:-.02em;margin:0 0 40px;max-width:15ch;
}
.steps{list-style:none;margin:0;padding:0;display:grid;gap:0}
.steps li{
  position:relative;padding:34px 30px 34px 130px;
  border-top:1px solid var(--edge);
  transition:background .5s var(--ease-soft);
}
.steps li:last-child{border-bottom:1px solid var(--edge)}
.steps li:hover{background:rgba(255,255,255,.017)}
.step-num{
  position:absolute;left:30px;top:30px;
  font-family:var(--serif);font-size:46px;line-height:1;color:var(--gold-dim);
  transition:color .5s var(--ease-soft);
}
.steps li:hover .step-num{color:var(--gold)}
.steps h3{font-size:19px;margin-bottom:8px;letter-spacing:-.01em}
.steps p{margin:0;color:var(--ink-2);font-size:14.5px;max-width:62ch}

/* The example output. It is the only raster image on the page, so it carries the
   whole image-search and social-preview story — keep it real, keep the caption
   descriptive, and never let it stretch past its own pixel width. */
.figure{margin:38px 0 0}
.figure img{
  display:block;width:100%;max-width:760px;height:auto;margin:0 auto;
  border:1px solid var(--edge);border-radius:4px;background:var(--film);
}
.figure figcaption{
  max-width:760px;margin:14px auto 0;color:var(--ink-3);
  font-size:13px;line-height:1.6;text-align:center;
}

/* Section lead-in. 70ch keeps the measure readable; the max-width also stops
   the paragraph from stretching across a 2560px window. */
.prose{max-width:70ch;margin:0 0 28px;color:var(--ink-2);font-size:15px;line-height:1.78}

/* The header is sticky, so an anchor jump would land underneath it without a
   scroll offset. 92px clears the bar at every width. */
html{scroll-padding-top:92px}

/* In-page jump list. The page runs past 1,600 words, so a reader needs a way
   past the tool, and the anchors give a crawler a second set of targets. */
.jump{
  display:flex;flex-wrap:wrap;align-items:center;gap:7px 16px;
  margin-top:34px;padding-top:20px;border-top:1px solid var(--edge);
}
.jump-label{
  font-family:var(--mono);font-size:10.5px;letter-spacing:.16em;
  text-transform:uppercase;color:var(--ink-3);
}
.jump a{
  font-size:13px;color:var(--ink-2);text-decoration:none;
  padding-bottom:2px;border-bottom:1px solid transparent;
  transition:color .3s var(--ease-soft),border-color .3s var(--ease-soft);
}
.jump a:hover,.jump a:focus-visible{color:var(--gold);border-bottom-color:var(--gold-dim)}

/* The comparison carries the whole "why this one" argument, so it runs on four
   axes rather than one. Each axis is its own row, which keeps the two columns
   aligned without a table — and each row collapses to a single column on a
   narrow screen, so nothing ever needs a horizontal scrollbar. */
.compare{display:grid;gap:2px}
.compare-row{display:grid;grid-template-columns:1fr 1fr;gap:2px}
.compare-row>span{
  position:relative;background:var(--film);border:1px solid var(--edge);
  padding:18px 22px 18px 46px;font-size:14px;line-height:1.6;color:var(--ink-2);
}
/* the mark is the fastest way to read which column you are in */
.compare-row>span::before{position:absolute;left:22px;top:17px;font-size:14px;line-height:1.6}
.compare-them::before{content:"\00d7";color:var(--ink-3)}
.compare-us::before{content:"\2713";color:var(--gold)}
.compare-row>span b{
  display:block;font-size:10.5px;font-weight:500;letter-spacing:.16em;
  text-transform:uppercase;margin-bottom:6px;
}
.compare-them b{color:var(--ink-3)}
.compare-us b{color:var(--gold)}
.compare-head>span{
  font-family:var(--serif);font-size:20px;line-height:1.25;
  padding:22px 22px 18px 46px;
}
.compare-head>span::before{top:25px}
.compare-head>span.compare-them{color:var(--ink-3)}
.compare-head>span.compare-us{color:var(--gold)}

.cards{display:grid;grid-template-columns:repeat(3,1fr);gap:2px}
.card{
  background:var(--film);border:1px solid var(--edge);padding:32px 26px;
  transition:background .5s var(--ease-soft),border-color .5s var(--ease-soft);
}
.card:hover{background:var(--film-2);border-color:var(--edge-2)}
.card h3{font-family:var(--serif);font-size:23px;margin-bottom:10px}
.card p{margin:0;color:var(--ink-2);font-size:14px;line-height:1.7}

#faq details{border-top:1px solid var(--edge)}
#faq details:last-of-type{border-bottom:1px solid var(--edge)}
#faq summary{
  cursor:pointer;padding:22px 4px;font-size:16px;font-weight:500;list-style:none;
  display:flex;align-items:center;justify-content:space-between;gap:16px;
  transition:color .35s var(--ease-soft);
}
#faq summary:hover{color:var(--gold-2)}
#faq summary::-webkit-details-marker{display:none}
#faq summary::after{
  content:"";flex:0 0 auto;width:10px;height:10px;
  border-right:1.5px solid var(--gold);border-bottom:1.5px solid var(--gold);
  transform:rotate(45deg) translate(-2px,-2px);
  transition:transform .4s var(--ease);
}
#faq details[open] summary::after{transform:rotate(225deg) translate(-2px,-2px)}
#faq details p{margin:0;padding:0 4px 26px;color:var(--ink-2);font-size:14.5px;max-width:74ch}
#faq details[open] p{animation:fadeUp .55s var(--ease) both}
@keyframes fadeUp{from{opacity:0;transform:translateY(8px)}to{opacity:1;transform:none}}

/* -------------------------------------------------------------- footer */

.site-foot{margin-top:130px;border-top:1px solid var(--edge);padding:38px 0 54px}
.foot-inner{display:flex;align-items:center;gap:14px;flex-wrap:wrap}
.site-foot p{margin:0;font-size:12.5px;color:var(--ink-3)}
.foot-lang{
  position:relative;margin-left:auto;padding:10px 6px;
  font-size:12.5px;color:var(--ink-3);text-decoration:none;
  transition:color .3s var(--ease-soft);
}
.foot-lang::after{
  content:"";position:absolute;left:6px;right:6px;bottom:7px;height:1px;
  background:currentColor;transform:scaleX(0);transform-origin:left center;
  transition:transform .42s var(--ease);
}
.foot-lang:hover,.foot-lang:focus-visible{color:var(--gold)}
.foot-lang:hover::after,.foot-lang:focus-visible::after{transform:scaleX(1)}
/* Dated byline. Google wants to see when a page last changed, and a human
   visitor wants the same thing — keep it in the footer, not in the hero. */
.foot-meta{margin:16px 0 0;font-size:12px;color:var(--ink-3);line-height:1.6}

/* ------------------------------------------------------ spec list */
/* Definition-style rows: label left, value right. Used for the video panel's
   capability list, where a table would be too heavy on a narrow screen. */
.spec{list-style:none;margin:0;padding:0;display:grid;gap:0;max-width:70ch}
.spec li{
  display:grid;grid-template-columns:minmax(110px,150px) 1fr;gap:16px;
  padding:11px 0;border-top:1px solid var(--edge);
}
.spec li:last-child{border-bottom:1px solid var(--edge)}
.spec b{color:var(--ink);font-weight:600;font-size:13.5px}
.spec span{color:var(--ink-2);font-size:13.5px;line-height:1.6}

/* four cards read better as 2x2 than as a 3+1 orphan row */
.cards-4{grid-template-columns:repeat(2,1fr)}

/* --------------------------------------------------------- video panel */

#videoRoot{
  background:var(--film-2);border:1px solid var(--edge-2);
  border-radius:var(--radius);padding:22px 22px 20px;margin:0 0 18px;
}
.vhead{display:flex;align-items:center;justify-content:space-between;margin-bottom:16px}
.vtitle{font-size:11px;font-weight:500;letter-spacing:.24em;text-transform:uppercase;color:var(--gold)}
.vprog{display:flex;flex-direction:column;gap:12px}
.vbar{height:3px;border-radius:999px;background:rgba(255,255,255,.07);overflow:hidden}
.vbar i{
  display:block;height:100%;width:0;border-radius:999px;
  background:linear-gradient(90deg,var(--gold-dim),var(--gold));
  transition:width .35s var(--ease-soft);
}
.vstatus{margin:0;font-size:12.5px;color:var(--ink-2);font-family:var(--mono)}
.verror{
  margin:0 0 12px;padding:13px 15px;border-radius:var(--radius-sm);
  background:rgba(212,85,63,.1);border:1px solid rgba(212,85,63,.35);
  color:#eda593;font-size:13px;
}
.vmeta{margin:0 0 14px;font-size:13px;color:var(--ink-2)}
.vtools{display:flex;align-items:center;gap:10px;margin-bottom:14px;flex-wrap:wrap}
/* .btn-ghost is full-width for the sidebar; inside the video panel it must
   size to its label, or Cancel / All / None stretch into empty bars */
.vhead .btn-ghost,.vtools .btn-ghost{width:auto;margin-top:0;padding:8px 16px}
.vcount{margin-left:auto;font-size:12px;color:var(--ink-3);font-family:var(--mono)}

.vshots{
  list-style:none;margin:0;padding:0;display:grid;gap:10px;
  grid-template-columns:repeat(auto-fill,minmax(148px,1fr));
  max-height:52vh;overflow:auto;padding-right:4px;
  scrollbar-width:thin;scrollbar-color:var(--edge-2) transparent;
}
.vshots::-webkit-scrollbar{width:9px}
.vshots::-webkit-scrollbar-thumb{background:var(--edge-2);border-radius:9px}
.vshot{
  border:1px solid var(--edge);border-radius:var(--radius-sm);
  background:var(--film);overflow:hidden;
  transition:border-color .3s var(--ease-soft),opacity .3s linear;
}
.vshot.is-off{opacity:.34}
.vshot label{display:block;position:relative;cursor:pointer}
.vshot input{position:absolute;width:1px;height:1px;opacity:0;pointer-events:none}
.vshot label::after{
  content:"";position:absolute;top:8px;right:8px;width:18px;height:18px;
  border-radius:50%;border:1.5px solid rgba(255,255,255,.55);
  background:rgba(0,0,0,.5);
  transition:background .25s var(--ease-soft),border-color .25s var(--ease-soft),box-shadow .25s var(--ease-soft);
}
.vshot:has(input:checked) label::after{
  background:var(--gold);border-color:var(--gold);
  box-shadow:inset 0 0 0 3px var(--film);
}
.vshot:has(input:focus-visible) label{outline:2px solid var(--gold);outline-offset:2px}
.vshot canvas{display:block;width:100%;height:auto;background:#000}
.vshot-meta{display:flex;align-items:baseline;justify-content:space-between;gap:8px;padding:8px 10px}
.vshot-meta b{font-size:11.5px;font-weight:500;color:var(--gold);font-family:var(--mono)}
.vshot-meta i{font-size:10.5px;font-style:normal;color:var(--ink-3);font-family:var(--mono)}
.vactions{display:flex;justify-content:flex-end;margin-top:16px}

/* ------------------------------------------------- CJK typography pass */

/* Chinese type needs different metrics from Latin: no negative tracking
   (CJK glyphs are already tight), looser leading, and no synthetic italic —
   a slanted Songti looks broken. Headings also run smaller, because a CJK
   glyph fills far more of its em box than a Latin one at the same size. */

:lang(zh) body{line-height:1.78}
:lang(zh) .eyebrow{letter-spacing:.1em;text-transform:none;font-size:12px}
:lang(zh) .hero-title{
  font-size:clamp(2.15rem,6vw,4.6rem);
  line-height:1.2;letter-spacing:0;
}
:lang(zh) .hero-title em{font-style:normal}
:lang(zh) .lede{line-height:1.85;max-width:34em}
:lang(zh) .head-nav a{letter-spacing:.02em;text-transform:none;font-size:13px}
:lang(zh) .jump-label{letter-spacing:.04em;text-transform:none;font-size:11px}
:lang(zh) .content h2{
  font-size:clamp(1.75rem,3.8vw,2.9rem);
  letter-spacing:0;max-width:20ch;line-height:1.3;
}
:lang(zh) .steps h3,:lang(zh) .card h3{letter-spacing:0}
:lang(zh) .steps p,:lang(zh) .card p,:lang(zh) #faq details p{line-height:1.85}
:lang(zh) .steps p{max-width:48ch}
:lang(zh) #faq details p{max-width:58ch}
:lang(zh) #faq summary{line-height:1.5}
:lang(zh) .dz-hint,:lang(zh) .stage-info,:lang(zh) .stage-foot{letter-spacing:0}
:lang(zh) .badges span{letter-spacing:0}
:lang(zh) .vtitle{letter-spacing:.1em;text-transform:none;font-size:12px}
/* uppercase is a no-op on CJK, and .16em tracking pulls Chinese labels apart */
:lang(zh) .compare-row>span b{letter-spacing:.04em;text-transform:none;font-size:11px}
:lang(zh) .vstatus,:lang(zh) .vcount,:lang(zh) .vshot-meta b{letter-spacing:0}

/* ---------------------------------------------------------- responsive */

@media (max-width:1080px){
  .hero-grid{grid-template-columns:minmax(0,1fr);gap:32px}
  .hero{padding:46px 0 26px}
  .hero-title{font-size:clamp(2.25rem,7vw,3.5rem)}
  /* single column, so the demo's height comes straight off the fold */
  .demo{max-width:370px;margin-bottom:18px}
}
@media (max-width:980px){
  .workspace{grid-template-columns:1fr}
  .panel{order:2}
  .stage{order:1}
  .cards{grid-template-columns:1fr}
  /* two 330px columns is where the comparison starts to feel cramped */
  .compare-row{grid-template-columns:1fr}
}
/* Single column on a short window (tablet landscape, small laptop) is the one
   case where the stacked hero plus the demo can push the CTA copy past the
   fold — give the vertical space back. Height, not width, is the trigger. */
@media (min-width:661px) and (max-width:1080px) and (max-height:860px){
  .hero{padding:26px 0 18px}
  .hero-title{font-size:clamp(2.1rem,5.4vw,2.9rem)}
  :lang(zh) .hero-title{font-size:clamp(2rem,5.2vw,2.9rem)}
  .lede{margin-top:18px}
  .badges{margin-top:18px}
  .demo{max-width:320px;margin-bottom:16px}
  .dropzone{padding:32px 20px}
}
@media (max-width:660px){
  .wrap{padding:0 18px}
  .hero{padding:36px 0 20px}
  .tool{padding:22px 18px 4px}
  .tool-frame{padding:16px}
  .dropzone{padding:40px 18px}
  .content{padding:76px 18px 0}
  .content h2{margin-bottom:28px}
  .head-nav{display:none}
  .steps li{padding:26px 20px}
  .step-num{position:static;display:block;font-size:34px;margin-bottom:10px}
  .preview{padding:12px;max-height:58vh}
  .stage-bar{flex-direction:column;align-items:stretch;gap:10px}
  .btn-primary{width:100%}
  .compare-row>span{padding:16px 18px 16px 40px}
  .compare-head>span{padding:20px 18px 16px 40px}
  .compare-row>span::before{left:18px}
  /* two columns of a spec row cannot survive 354px — stack them */
  .spec li{grid-template-columns:1fr;gap:3px}
  /* the jump list drops its label so it stays on two rows */
  .jump{gap:6px 12px;margin-top:26px;padding-top:16px}
  .jump-label{display:none}
}
