/* ============================================================================
   Text Behind Subject — editor UI

   Palette note: the accent is a cool lime, so the neutrals carry a trace of
   green rather than of red. A warm grey shell fights lime; a faintly green one
   lets it sit as the only saturated thing on screen.
   ========================================================================== */

:root{
  /* accent ---------------------------------------------------------------- */
  --brand:#CFFF49;
  --brand-hi:#dcff70;
  --brand-lo:#b9e83a;
  /* Text sitting ON the accent. Lime is a light colour, so this is near-black
     — 14:1 against #CFFF49. White on lime would be unreadable. */
  --brand-ink:#0a0a0a;
  --brand-a08:rgba(207,255,73,.08);
  --brand-a14:rgba(207,255,73,.14);
  --brand-a35:rgba(207,255,73,.35);
  --brand-a50:rgba(207,255,73,.5);

  /* neutral shell ---------------------------------------------------- */
  --ink:#0a0a0a;
  --stage:#0e0f0e;
  --panel:#141414;
  --panel-2:#1a1a1a;
  --raise:#212121;
  --raise-hi:#2a2a2a;
  /* Recessed surface for anything you type into. */
  --sunk:#0d0d0d;

  --line:rgba(255,255,255,.075);
  --line-2:rgba(255,255,255,.15);

  --text:#f2f2f2;
  --muted:#8a908a;
  --faint:#5f655f;
  --danger:#ff6b5e;
  --ok:#5fd47a;
  --warn:#e0b341;

  --ui:'Manrope',system-ui,-apple-system,sans-serif;

  --r-sm:10px;
  --r-md:14px;
  --r-lg:20px;
  --rail:376px;

  /* Elevation. The inset hairline along the top edge is what separates a
     raised surface from a flat one on a dark UI — more than the drop shadow. */
  --edge:inset 0 1px 0 rgba(255,255,255,.055);
  --shadow-1:0 1px 2px rgba(0,0,0,.45);
  --shadow-2:0 4px 16px -4px rgba(0,0,0,.55);
  --shadow-3:0 24px 60px -18px rgba(0,0,0,.78);
  --ring:0 0 0 3px var(--brand-a35);

  --spring:cubic-bezier(.34,1.4,.5,1);
  --ease:cubic-bezier(.22,.61,.36,1);
}

*{box-sizing:border-box;margin:0;padding:0}
/* `[hidden]{display:none}` lives in the UA sheet, so ANY author `display`
   declaration beats it — `.progress`, `.field` and `.cutout-row` are all
   `display:flex`, which silently un-hides them. Author-level and !important is
   the only thing every one of those rules loses to. */
[hidden]{display:none !important}
html,body{height:100%}
body{
  background:var(--ink);
  color:var(--text);
  font-family:var(--ui);
  font-size:14.5px;
  -webkit-font-smoothing:antialiased;
  -moz-osx-font-smoothing:grayscale;
  overflow:hidden;
}
svg{width:100%;height:100%;fill:none;stroke:currentColor;stroke-width:1.7;stroke-linecap:round;stroke-linejoin:round}

::selection{background:var(--brand-a35);color:#fff}

.app{
  display:grid;
  grid-template-columns:1fr var(--rail);
  grid-template-rows:60px 1fr;
  grid-template-areas:"bar bar" "stage rail";
  height:100dvh;
}

/* ---------- top bar ---------- */
.bar{
  grid-area:bar;
  display:flex;align-items:center;justify-content:space-between;
  padding:0 16px 0 18px;
  background:var(--panel);
  border-bottom:1px solid var(--line);
  box-shadow:var(--edge),0 1px 0 rgba(0,0,0,.5);
  z-index:5;
}
.brand{display:flex;align-items:center;gap:11px;text-decoration:none;color:inherit}
a.brand{
  border-radius:var(--r-sm);
  padding:5px 9px 5px 6px;
  margin-left:-6px;
  transition:background .18s var(--ease);
}
a.brand:hover{background:var(--panel-2)}
/* Same four-rhombus mark as the landing page. The global `svg` rule sets
   fill:none / stroke:currentColor for the icon set, so both have to be undone
   here or the glyph renders as four hairline outlines. */
.mark{
  width:23px;height:23px;
  flex:none;
  fill:var(--brand);
  stroke:none;
}
.name{font-weight:700;letter-spacing:-.025em;font-size:15px}
.name em{font-style:normal;color:var(--muted);font-weight:500}

.bar-actions{display:flex;align-items:center;gap:7px}
.sep{width:1px;height:22px;background:var(--line);margin:0 5px}

/* ---------- buttons ---------- */
.btn{
  font:inherit;font-weight:600;letter-spacing:-.01em;color:var(--text);
  background:var(--raise);
  border:1px solid var(--line);
  border-radius:var(--r-sm);
  padding:0 14px;height:36px;
  display:inline-flex;align-items:center;justify-content:center;gap:7px;
  cursor:pointer;white-space:nowrap;
  box-shadow:var(--edge),var(--shadow-1);
  transition:transform .22s var(--spring),background .16s var(--ease),
             border-color .16s var(--ease),box-shadow .16s var(--ease),opacity .16s;
}
.btn:hover:not(:disabled){background:var(--raise-hi);border-color:var(--line-2)}
.btn:active:not(:disabled){transform:scale(.96)}
.btn:disabled{opacity:.32;cursor:not-allowed;box-shadow:none}
.btn:focus-visible{outline:none;box-shadow:var(--edge),var(--ring)}
.btn svg{width:17px;height:17px}

.btn.ghost{background:transparent;box-shadow:none}
.btn.ghost:hover:not(:disabled){background:var(--raise)}

.btn.primary{
  background:linear-gradient(180deg,var(--brand-hi),var(--brand));
  color:var(--brand-ink);
  border-color:transparent;
  box-shadow:inset 0 1px 0 rgba(255,255,255,.28),0 2px 14px -3px var(--brand-a50);
}
.btn.primary:hover:not(:disabled){
  background:linear-gradient(180deg,#e6ff8c,var(--brand-hi));
  box-shadow:inset 0 1px 0 rgba(255,255,255,.3),0 4px 18px -3px var(--brand-a50);
}
.btn.primary:disabled{background:var(--raise);color:var(--text);box-shadow:none}

.btn.tiny{height:32px;padding:0 12px;font-size:13px;border-radius:9px}
.btn.lg{height:46px;padding:0 26px;font-size:15px;border-radius:var(--r-md)}
.btn.danger:hover:not(:disabled){color:var(--danger);border-color:rgba(255,107,94,.4);background:rgba(255,107,94,.08)}

/* ---------- stage ---------- */
.stage{
  grid-area:stage;
  position:relative;
  display:grid;place-items:center;
  padding:34px;
  /* Kept deliberately faint. Lime carries much further than a warm accent at
     the same alpha — anything above ~.05 here tints the whole stage olive and
     the image on the canvas reads colour-cast. */
  background:
    radial-gradient(110% 80% at 50% 0%,rgba(207,255,73,.045),transparent 58%),
    var(--stage);
  overflow:hidden;
}
.stage::before{
  content:"";position:absolute;inset:0;
  background-image:linear-gradient(var(--line) 1px,transparent 1px),linear-gradient(90deg,var(--line) 1px,transparent 1px);
  background-size:48px 48px;
  opacity:.32;
  mask-image:radial-gradient(70% 60% at 50% 50%,#000,transparent);
  pointer-events:none;
}
.stage::after{
  content:"";position:absolute;inset:0;pointer-events:none;
  background:radial-gradient(115% 95% at 50% 45%,transparent 42%,rgba(0,0,0,.5) 100%);
}
.canvas-wrap{
  position:relative;
  border-radius:var(--r-md);
  overflow:hidden;
  box-shadow:var(--shadow-3),0 0 0 1px rgba(255,255,255,.06);
  line-height:0;
  transition:opacity .3s var(--ease);
}
/* Transparent mode: the canvas has real alpha, so the wrap shows through it.
   Checkerboard is the only honest way to tell "transparent" from "white". */
.canvas-wrap[data-alpha="true"]{
  background-color:#171717;
  background-image:
    linear-gradient(45deg,#232323 25%,transparent 25%,transparent 75%,#232323 75%),
    linear-gradient(45deg,#232323 25%,transparent 25%,transparent 75%,#232323 75%);
  background-size:20px 20px;
  background-position:0 0,10px 10px;
}
#canvas{display:block;max-width:100%;max-height:100%;touch-action:none;cursor:default}
#canvas.grabbing{cursor:grabbing}
#canvas.grab{cursor:grab}
#canvas.spin{cursor:crosshair}
#canvas.rotating{cursor:grabbing}
/* Repair mode owns the canvas, so it wins over every text-layer cursor above. */
.canvas-wrap[data-repair="true"] #canvas{cursor:crosshair}
.app[data-has-image="false"] .canvas-wrap{opacity:0;pointer-events:none}

/* dropzone */
.drop{
  position:absolute;inset:26px;
  border:1.5px dashed rgba(255,255,255,.09);
  border-radius:var(--r-lg);
  display:grid;place-items:center;
  text-align:center;
  transition:border-color .2s var(--ease),background .2s var(--ease),
             opacity .28s var(--ease),transform .28s var(--spring);
}
.drop.over{
  border-color:var(--brand);
  background:var(--brand-a08);
  box-shadow:inset 0 0 80px -20px var(--brand-a35);
}
.app[data-has-image="true"] .drop{opacity:0;pointer-events:none;transform:scale(.97)}
/* A real card, so the eye has something to land on instead of a void. */
.drop-inner{
  display:flex;flex-direction:column;align-items:center;gap:16px;
  max-width:440px;
  padding:38px 44px 40px;
  border-radius:var(--r-lg);
  background:rgba(22,22,22,.82);
  border:1px solid var(--line);
  backdrop-filter:blur(14px);
  -webkit-backdrop-filter:blur(14px);
  box-shadow:var(--edge),var(--shadow-3);
}
.drop-icon{
  width:58px;height:58px;
  display:grid;place-items:center;
  border-radius:18px;
  background:var(--brand-a08);
  border:1px solid var(--brand-a14);
  color:var(--brand);
}
.drop-icon svg{width:27px;height:27px}
.drop h1{font-size:22px;font-weight:700;letter-spacing:-.028em;line-height:1.25}
.drop p{color:var(--muted);line-height:1.55;font-size:14px}
.drop .hint{color:var(--faint);font-size:12.5px;margin-top:-4px}

/* progress */
.progress{
  position:absolute;inset:auto auto 30px 50%;transform:translateX(-50%);
  display:flex;align-items:center;gap:14px;
  background:rgba(20,20,20,.92);
  backdrop-filter:blur(16px);
  -webkit-backdrop-filter:blur(16px);
  border:1px solid var(--line-2);
  border-radius:var(--r-md);
  padding:13px 20px 13px 14px;
  box-shadow:var(--edge),var(--shadow-3);
  max-width:min(520px,90%);
  z-index:4;
}
.ring{width:34px;height:34px;flex:none}
.ring circle{fill:none;stroke-width:3.5;stroke-linecap:round}
.ring .track{stroke:rgba(255,255,255,.1)}
.ring .fill{
  stroke:var(--brand);
  stroke-dasharray:119.4;
  stroke-dashoffset:119.4;
  transform:rotate(-90deg);transform-origin:center;
  transition:stroke-dashoffset .3s var(--ease);
  filter:drop-shadow(0 0 5px var(--brand-a50));
}
.progress-text{display:flex;flex-direction:column;gap:2px;min-width:0}
.progress-text strong{font-size:13.5px;font-weight:600;letter-spacing:-.01em}
.progress-text span{font-size:12px;color:var(--muted);line-height:1.4}

/* ---------- rail ---------- */
.rail{
  grid-area:rail;
  background:var(--panel);
  box-shadow:inset 1px 0 0 rgba(255,255,255,.03);
  border-left:1px solid var(--line);
  display:flex;flex-direction:column;
  overflow-y:auto;
  overscroll-behavior:contain;
  scrollbar-width:thin;
  scrollbar-color:#2c2c2c transparent;
}
.rail::-webkit-scrollbar{width:10px}
.rail::-webkit-scrollbar-thumb{
  background:#2c2c2c;border-radius:6px;border:3px solid var(--panel);
}
.rail::-webkit-scrollbar-thumb:hover{background:#3a3a3a}
.rail::-webkit-scrollbar-track{background:transparent}

.panel{padding:16px 18px 20px;border-bottom:1px solid var(--line)}
/* A hairline of light on the leading edge separates stacked sections far more
   cleanly than a heavier divider would. */
.panel + .panel{box-shadow:inset 0 1px 0 rgba(255,255,255,.03)}
.panel.grow{flex:1}
/* Dimming the whole section also hides the sentence explaining WHY it is
   disabled. Fade the controls, keep the heading and the note legible. */
.panel[data-disabled="true"]{pointer-events:none}
.panel[data-disabled="true"] > *:not(.panel-head):not(.note){opacity:.3}
.panel[data-disabled="true"] .panel-head h2{color:var(--faint)}
.panel[data-disabled="true"] .panel-head h2::before{opacity:.3}
.panel[data-disabled="true"] .panel-head .btn{opacity:.3}
.panel[data-disabled="true"] > .note{color:var(--muted)}

/* Sticky section headers: the rail is long, and losing track of which panel
   you are in is the main way it gets confusing. */
.panel-head{
  position:sticky;top:0;z-index:3;
  display:flex;align-items:center;justify-content:space-between;gap:10px;
  min-height:30px;
  margin:-16px -18px 12px;
  padding:13px 18px 10px;
  /* Opaque. A gradient fading to transparent let the layer-ops row scroll up
     underneath and render straight through the title. */
  background:var(--panel);
  box-shadow:0 1px 0 var(--line), 0 8px 12px -10px rgba(0,0,0,.9);
}
.panel-head h2{
  font-size:11.5px;font-weight:700;letter-spacing:.11em;
  text-transform:uppercase;color:var(--muted);
  display:flex;align-items:center;gap:8px;
}
/* A small accent tick marks the section title without adding a border. */
.panel-head h2::before{
  content:"";width:3px;height:11px;border-radius:2px;
  background:var(--brand);opacity:.75;flex:none;
}

/* layer list */
.layers{list-style:none;display:flex;flex-direction:column;gap:6px}
.layer{
  position:relative;
  display:flex;align-items:center;gap:10px;
  padding:9px 11px;
  background:var(--panel-2);
  border:1px solid transparent;
  border-radius:var(--r-sm);
  cursor:pointer;
  overflow:hidden;
  transition:background .16s var(--ease),border-color .16s var(--ease),transform .2s var(--spring);
}
.layer:hover{background:var(--raise)}
.layer:active{transform:scale(.985)}
.layer[aria-selected="true"]{
  border-color:var(--brand-a35);
  background:var(--brand-a08);
}
/* Accent bar on the selected row — readable at a glance from across the rail. */
.layer[aria-selected="true"]::before{
  content:"";position:absolute;left:0;top:0;bottom:0;width:3px;
  background:var(--brand);
}
.layer .glyph{
  width:26px;height:26px;flex:none;border-radius:8px;
  background:var(--raise);
  display:grid;place-items:center;
  font-size:13px;font-weight:700;color:var(--muted);
  transition:background .16s var(--ease),color .16s var(--ease);
}
.layer[aria-selected="true"] .glyph{
  background:var(--brand);color:var(--brand-ink);
  box-shadow:0 2px 8px -2px var(--brand-a50);
}
.layer .label{
  flex:1;min-width:0;
  font-size:13.5px;font-weight:500;
  white-space:nowrap;overflow:hidden;text-overflow:ellipsis;
}
.layer .meta{font-size:12px;color:var(--faint);flex:none}
.empty{color:var(--faint);font-size:13px;padding:6px 2px}

/* fields */
.field{display:flex;flex-direction:column;gap:6px;margin-bottom:13px;min-width:0}
.row{display:grid;grid-template-columns:1fr 1fr;gap:12px}
label{font-size:12px;font-weight:600;color:var(--muted);display:flex;justify-content:space-between;align-items:center}
output{font-variant-numeric:tabular-nums;color:var(--text);font-weight:600}

input[type=text],textarea,select{
  font:inherit;color:var(--text);
  background:var(--sunk);
  border:1px solid var(--line);
  border-radius:var(--r-sm);
  padding:10px 12px;
  width:100%;
  box-shadow:inset 0 1px 2px rgba(0,0,0,.45);
  transition:border-color .16s var(--ease),background .16s var(--ease),box-shadow .16s var(--ease);
}
input[type=text]:hover,textarea:hover,select:hover{border-color:var(--line-2)}
textarea{resize:vertical;line-height:1.45;min-height:64px;font-size:13.5px}
textarea::placeholder{color:var(--faint)}
select{appearance:none;cursor:pointer;padding-right:30px;
  background-image:url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 12'><path d='M3 4.6 6 7.6l3-3' fill='none' stroke='%238a908a' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'/></svg>");
  background-repeat:no-repeat;background-position:right 9px center;background-size:12px;
}
input:focus-visible,textarea:focus-visible,select:focus-visible{
  outline:none;border-color:var(--brand);background:#111;
  box-shadow:inset 0 1px 2px rgba(0,0,0,.45),var(--ring);
}

/* range ------------------------------------------------------------------
   The track is filled up to the thumb. Firefox does this natively with
   ::-moz-range-progress; WebKit has no equivalent, so js/range-fill.js keeps
   a --fill percentage on the element and the gradient reads it. */
input[type=range]{
  -webkit-appearance:none;appearance:none;
  width:100%;height:18px;background:transparent;cursor:pointer;
  --fill:0%;
}
input[type=range]::-webkit-slider-runnable-track{
  height:5px;border-radius:3px;
  background:linear-gradient(to right,
    var(--brand) 0 var(--fill),
    #2c2c2c var(--fill) 100%);
}
input[type=range]::-moz-range-track{height:5px;border-radius:3px;background:#2c2c2c}
input[type=range]::-moz-range-progress{height:5px;border-radius:3px;background:var(--brand)}
input[type=range]::-webkit-slider-thumb{
  -webkit-appearance:none;appearance:none;
  width:15px;height:15px;border-radius:50%;
  background:#fff;margin-top:-5px;
  box-shadow:0 1px 4px rgba(0,0,0,.6),0 0 0 1px rgba(0,0,0,.25);
  transition:transform .2s var(--spring),box-shadow .2s var(--ease);
}
input[type=range]::-moz-range-thumb{
  width:15px;height:15px;border:none;border-radius:50%;
  background:#fff;box-shadow:0 1px 4px rgba(0,0,0,.6),0 0 0 1px rgba(0,0,0,.25);
}
input[type=range]:hover::-webkit-slider-thumb{transform:scale(1.16);box-shadow:0 1px 6px rgba(0,0,0,.6),0 0 0 5px var(--brand-a14)}
input[type=range]:active::-webkit-slider-thumb{transform:scale(.96)}
input[type=range]:focus-visible{outline:none}
input[type=range]:focus-visible::-webkit-slider-thumb{box-shadow:0 1px 4px rgba(0,0,0,.6),var(--ring)}
.field.slider label{margin-bottom:-2px}

/* editable readout paired with a slider */
.num{display:inline-flex;align-items:center;gap:2px;color:var(--text);font-weight:600}
.num em{font-style:normal;color:var(--muted);font-size:11px}
.num input[type=number]{
  width:58px;
  font:inherit;font-size:12.5px;font-weight:600;
  font-variant-numeric:tabular-nums;
  color:var(--text);
  background:var(--panel-2);
  border:1px solid var(--line);
  border-radius:7px;
  padding:3px 6px;
  text-align:right;
  box-shadow:inset 0 1px 2px rgba(0,0,0,.4);
  appearance:textfield;
  transition:border-color .16s var(--ease),background .16s var(--ease),box-shadow .16s var(--ease);
}
/* Spinners are 20px of chrome on a 54px field, and the slider already covers
   coarse adjustment. */
.num input[type=number]::-webkit-outer-spin-button,
.num input[type=number]::-webkit-inner-spin-button{appearance:none;margin:0}
.num input[type=number]:hover{border-color:var(--line-2)}
.num input[type=number]:focus-visible{outline:none;border-color:var(--brand);background:var(--raise);box-shadow:var(--ring)}

/* colour swatch */
.swatch{position:relative;height:38px}
.swatch input[type=color]{position:absolute;inset:0;opacity:0;width:100%;height:100%;cursor:pointer}
.swatch span{
  position:absolute;inset:0;
  border-radius:var(--r-sm);
  border:1px solid var(--line-2);
  box-shadow:inset 0 1px 3px rgba(0,0,0,.35),inset 0 0 0 1px rgba(255,255,255,.05);
  pointer-events:none;
  transition:transform .2s var(--spring),box-shadow .2s var(--ease);
}
.swatch:hover span{transform:scale(1.03)}
.swatch input[type=color]:focus-visible + span{box-shadow:var(--ring)}

/* details */
.more{margin:2px 0 14px;border-top:1px solid var(--line);padding-top:12px}
.more summary{
  font-size:12.5px;font-weight:600;color:var(--muted);
  cursor:pointer;list-style:none;padding:3px 0;margin-bottom:10px;
  display:flex;align-items:center;gap:7px;
  border-radius:6px;
  transition:color .16s var(--ease);
}
.more summary:hover{color:var(--text)}
.more summary::-webkit-details-marker{display:none}
.more summary::before{
  content:"";width:0;height:0;flex:none;
  border-left:5px solid currentColor;
  border-top:3.5px solid transparent;
  border-bottom:3.5px solid transparent;
  transition:transform .2s var(--ease);
}
.more[open] summary{color:var(--text)}
.more[open] summary::before{transform:rotate(90deg)}
.more summary:focus-visible{outline:none;box-shadow:var(--ring)}

.layer-ops{display:grid;grid-template-columns:repeat(4,1fr);gap:6px}
.layer-ops .btn{padding:0 4px;font-size:11.5px}

/* segmented radio group — background modes */
.seg{display:grid;grid-template-columns:1fr 1fr;gap:6px;margin-bottom:13px}
.seg-opt{display:block;position:relative}
.seg-opt input{position:absolute;inset:0;width:100%;height:100%;opacity:0;margin:0;cursor:pointer}
.seg-opt span{
  display:flex;align-items:center;justify-content:center;
  height:36px;border-radius:var(--r-sm);
  background:var(--panel-2);
  border:1px solid var(--line);
  font-size:13px;font-weight:600;color:var(--muted);
  pointer-events:none;
  transition:background .16s var(--ease),border-color .16s var(--ease),
             color .16s var(--ease),transform .2s var(--spring),box-shadow .16s var(--ease);
}
.seg-opt:hover span{background:var(--raise);color:var(--text)}
.seg-opt:active span{transform:scale(.97)}
.seg-opt input:checked+span{
  background:var(--brand-a14);
  border-color:var(--brand-a35);
  color:var(--text);
  box-shadow:inset 0 1px 0 rgba(255,255,255,.06);
}
.seg-opt input:focus-visible+span{border-color:var(--brand);box-shadow:var(--ring)}

/* cutout panel */
.cutout-row{display:flex;gap:6px;margin-bottom:12px}
.cutout-row .btn{flex:1}
/* `.check` IS a <label>, and the base label rule sets space-between — which
   threw the toggle and its text to opposite edges of the rail. */
.check{
  display:flex;align-items:center;justify-content:flex-start;gap:11px;
  cursor:pointer;
  font-size:13.5px;color:var(--text);font-weight:500;
  padding:5px 0;
}
.check input{
  appearance:none;width:34px;height:19px;border-radius:999px;
  background:#2c2c2c;position:relative;cursor:pointer;flex:none;
  box-shadow:inset 0 1px 2px rgba(0,0,0,.4);
  transition:background .2s var(--ease);
}
.check input::after{
  content:"";position:absolute;top:2.5px;left:2.5px;
  width:14px;height:14px;border-radius:50%;background:var(--muted);
  box-shadow:0 1px 2px rgba(0,0,0,.4);
  transition:transform .25s var(--spring),background .2s var(--ease);
}
.check input:checked{background:var(--brand-a50)}
.check input:checked::after{transform:translateX(15px);background:var(--brand)}
.check input:focus-visible{outline:none;box-shadow:inset 0 1px 2px rgba(0,0,0,.4),var(--ring)}
.note{font-size:12.5px;color:var(--faint);line-height:1.6;margin:2px 0 12px}

/* Batch queue */
.batch-list{list-style:none;display:flex;flex-direction:column;gap:4px;max-height:210px;overflow-y:auto;margin-bottom:10px}
.batch-list:empty{display:none}
.batch-item{
  display:flex;align-items:center;gap:8px;padding:7px 9px;border-radius:9px;
  background:var(--panel-2);font-size:12px;min-width:0;
}
.batch-item .dot{width:7px;height:7px;border-radius:50%;background:var(--faint);flex:none}
.batch-item[data-status="working"] .dot{background:var(--brand);animation:pulse 1s ease-in-out infinite}
.batch-item[data-status="done"] .dot{background:var(--ok)}
.batch-item[data-status="failed"] .dot{background:var(--danger)}
.batch-item .nm{flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.batch-item .sz{color:var(--faint);font-size:11px;flex:none}
/* Shape mismatch is the one batch failure that is otherwise silent. */
.batch-item[data-drift="true"] .sz{color:var(--warn)}
@keyframes pulse{0%,100%{opacity:1}50%{opacity:.35}}
.check small{color:var(--faint);font-size:12px;font-weight:400;margin-left:5px}
.linkish{
  font:inherit;font-size:12px;font-weight:600;
  background:none;border:none;padding:2px 3px;margin:-2px -3px;cursor:pointer;
  border-radius:5px;
  color:var(--brand);opacity:.85;
  transition:opacity .16s var(--ease);
}
.linkish:hover{opacity:1;text-decoration:underline}
.linkish:focus-visible{outline:none;box-shadow:var(--ring);opacity:1}
.linkish.danger{color:var(--danger)}
.label-actions{display:inline-flex;align-items:center;gap:10px}

.keys{
  margin-top:auto;
  background:var(--sunk);
  border-top:1px solid var(--line);
  display:flex;flex-wrap:wrap;gap:7px 14px;
  padding:14px 18px 18px;
  font-size:11.5px;color:var(--faint);
}
.keys span{display:flex;align-items:center;gap:6px}
kbd{
  font:inherit;font-size:10.5px;
  background:var(--panel-2);border:1px solid var(--line);
  border-bottom-color:rgba(0,0,0,.5);
  border-radius:5px;padding:2px 5px;color:var(--muted);
}

/* toast */
.toast{
  position:fixed;bottom:26px;left:50%;
  transform:translate(-50%,14px);
  background:rgba(33,33,33,.96);
  backdrop-filter:blur(14px);
  -webkit-backdrop-filter:blur(14px);
  border:1px solid var(--line-2);
  border-left:3px solid var(--brand);
  border-radius:var(--r-md);
  padding:12px 20px;font-size:13.5px;font-weight:500;
  box-shadow:var(--edge),var(--shadow-3);
  opacity:0;pointer-events:none;
  transition:opacity .25s var(--ease),transform .3s var(--spring);
  z-index:20;max-width:82vw;text-align:center;
}
.toast.show{opacity:1;transform:translate(-50%,0)}
.toast.error{border-left-color:var(--danger);color:#ffb3ac}

@media (max-width:1080px){
  .app{grid-template-columns:1fr;grid-template-rows:60px minmax(320px,1fr) auto;grid-template-areas:"bar" "stage" "rail"}
  .rail{border-left:none;border-top:1px solid var(--line);max-height:52dvh}
  .stage{padding:18px}
  .bar{padding:0 12px}
  .name{display:none}
}
@media (prefers-reduced-motion:reduce){
  *{transition-duration:.01ms !important;animation-duration:.01ms !important}
}
