/* Pixi Studio — particles module: texture strip, colour stops, draw pad, zoom.
   The shape grid and the uploaded-file strip moved to core.css when the effects
   page started picking its source image with the same component. */

  /* One row per shape in the mix, each carrying its own size. A column, not a
     wrapped strip: the sliders only mean anything stacked, where their handles
     line up and you can see one shape against another at a glance. */
  /* ---- the emitter stack -----------------------------------------------------
     A card per emitter, in draw order, top one in front. It sits under the hero
     because the hero shows the SELECTED emitter — you read what this one is,
     then that it is one of several. */
  .lstack{padding:10px 13px 11px;border-bottom:1px solid var(--line)}
  .lhead{display:flex;align-items:baseline;gap:4px 8px;flex-wrap:wrap;margin-bottom:6px}
  .lhead .lbl{font-size:10.5px;text-transform:uppercase;letter-spacing:.6px;color:var(--muted-2);font-weight:700}
  .lhead .cnt{font-family:var(--mono);font-size:10px;color:var(--muted-2);opacity:.8}
  .lhead .hint{font-size:10.5px;color:var(--muted-2);opacity:.85;flex:1 1 100%}
  .lcards{display:flex;flex-direction:column;gap:4px}
  .lcard{display:flex;align-items:center;gap:6px;padding:5px 6px;cursor:pointer;
    background:#10151d;border:1px solid var(--line);border-radius:8px;
    transition:border-color .12s ease,background .12s ease}
  .lcard:hover{background:#151c26}
  .lcard.active{border-color:var(--accent);background:#161f2b}
  .lcard.off{opacity:.5}
  .lcard.dragging{opacity:.4}
  /* The drop target, marked on its leading edge — a full outline reads as
     "selected", which is a different thing and already taken. */
  .lcard.over{border-top-color:var(--accent);box-shadow:0 -2px 0 -1px var(--accent)}
  .lcard .grip{color:var(--muted-2);font-size:10px;line-height:1;cursor:grab;flex:0 0 auto;letter-spacing:-2px}
  .lcard .sw{width:16px;height:16px;border-radius:4px;flex:0 0 auto;border:1px solid rgba(255,255,255,.14)}
  .lcard .nm{font-size:11.5px;color:var(--muted);font-weight:600;flex:1 1 auto;min-width:0;
    overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
  .lcard.active .nm{color:var(--text,#e6edf6)}
  .lcard .cnt{font-style:normal;font-family:var(--mono);font-size:9.5px;color:var(--muted-2);flex:0 0 auto;opacity:.75}
  .lcard .acts{display:flex;align-items:center;gap:1px;flex:0 0 auto}
  .lcard .acts button{background:transparent;border:0;color:var(--muted-2);cursor:pointer;
    font-size:10px;line-height:1;padding:3px 3px;font-family:var(--sans);border-radius:4px}
  .lcard .acts button:hover:not(:disabled){color:var(--accent);background:rgba(94,208,255,.1)}
  .lcard .acts button:disabled{opacity:.25;cursor:default}
  .lcard .acts .switch{margin:0 2px}
  .lcard .acts button:last-child:hover:not(:disabled){color:var(--bad);background:rgba(255,107,107,.1)}
  /* Six of these stack up in a narrow panel, so the toggle is two-thirds size. */
  .switch.sm{width:24px;height:14px}
  .switch.sm .tk:before{width:10px;height:10px;left:2px;top:2px}
  .switch.sm input:checked + .tk:before{transform:translateX(10px)}
  .btn.addl{margin-top:6px;font-size:11px;padding:5px 8px}

  .tex-picked{display:flex;flex-direction:column;gap:4px;margin-top:8px}
  .tex-picked .head{display:flex;flex-wrap:wrap;align-items:baseline;gap:4px 10px;margin-bottom:1px}
  .tex-picked .hint{font-size:10.5px;color:var(--muted-2);opacity:.85}
  .tex-picked .hint kbd{background:#0c1017;border:1px solid var(--line);border-radius:4px;
    padding:0 4px;font-family:var(--mono);font-size:10px;color:var(--accent)}
  .tex-picked .lbl{font-size:10.5px;text-transform:uppercase;letter-spacing:.6px;color:var(--muted-2);font-weight:700}
  .tex-picked .chipx{display:flex;align-items:center;gap:7px;background:#10151d;border:1px solid var(--line);
    border-radius:7px;padding:4px 7px 4px 4px}
  .tex-picked .chipx canvas{border-radius:4px;display:block;flex:0 0 auto}
  .tex-picked .chipx b{font-size:11px;color:var(--muted);font-weight:600;flex:0 0 58px;
    overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
  /* A filename needs more room than "star" does, and it is the only row with a
     Remove button, so it gives some of the slider back to pay for both. */
  .tex-picked .chipx.own b{flex:0 0 82px}
  .tex-picked .chipx input[type=range]{flex:1 1 auto;min-width:36px;accent-color:var(--accent);height:18px}
  .tex-picked .chipx .v{font-family:var(--mono);font-size:10.5px;color:var(--muted-2);width:38px;text-align:right;flex:0 0 auto}
  .tex-picked .chipx button{background:transparent;border:0;color:var(--muted-2);font-size:11px;cursor:pointer;padding:0 2px;font-family:var(--sans);flex:0 0 auto}
  .tex-picked .chipx .x:hover{color:var(--bad)}
  /* The reset only earns its place once there is something to reset. */
  .tex-picked .chipx .rst{color:transparent;cursor:default;pointer-events:none}
  .tex-picked .chipx.tuned b{color:var(--accent)}
  .tex-picked .chipx.tuned .rst{color:var(--muted-2);cursor:pointer;pointer-events:auto}
  .tex-picked .chipx.tuned .rst:hover{color:var(--accent)}
  .stop-row{display:flex;align-items:center;gap:8px;margin:7px 0}
  .stop-row input[type=range]{flex:1;accent-color:var(--accent);height:18px}
  .stop-row input[type=color]{width:36px;height:24px;border:1px solid var(--line);background:transparent;border-radius:6px;padding:0;cursor:pointer;flex:0 0 auto}
  .stop-row .hexin{width:64px;padding:3px 5px}
  .stop-row{flex-wrap:wrap}
  .stop-row .arow{display:flex;align-items:center;gap:7px;flex:1 0 100%;margin:-2px 0 2px}
  .stop-row .alab{font-size:11px;color:var(--muted-2);flex:0 0 auto}
  .stop-row .aval{font-size:10.5px;color:var(--muted-2);font-family:var(--mono);width:30px;text-align:right;flex:0 0 auto}
  .stop-row .ar{flex:1}
  .stop-row .tval{font-family:var(--mono);font-size:10.5px;color:var(--muted-2);width:30px;text-align:right;flex:0 0 auto}
  .stop-row .rm{background:transparent;border:0;color:var(--muted);cursor:pointer;font-size:13px;padding:2px 5px;flex:0 0 auto}
  .stop-row .rm:hover{color:var(--bad)}
  /* Zoom sits at the right of the particles preview bar, where the effects page
     keeps its resolution segment. The hint beside it gives way first. */
  #particlesPage .prev-bar .hint{min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
  .zoomctl{display:flex;align-items:center;gap:8px;flex:0 0 auto}
  .zoomctl .zl{font-size:10px;text-transform:uppercase;letter-spacing:.7px;font-weight:700;color:var(--muted-2)}
  .zoomctl input[type=range]{width:112px;accent-color:var(--accent)}
  .zoomctl b{font-family:var(--mono);font-size:11.5px;color:var(--ink);
    min-width:52px;text-align:right;font-variant-numeric:tabular-nums}
  /* The timeline sits under the canvas, mirroring the preview bar above it, and
     is the full width because the scrubber is the point of the row. Its right end
     is where the credit badge is pinned, so on this page the badge moves up by
     exactly this height — the two numbers are kept together on purpose. */
  body.on-particles{--credit-bottom:56px}
  .time-bar{display:flex;align-items:center;gap:8px;padding:8px 12px;min-height:42px;
    border-top:1px solid var(--line);background:#0b0f16;flex:0 0 auto}
  .time-bar .btn.mini{padding:5px 10px;font-size:11.5px;flex:0 0 auto}
  .time-bar #pRevBtn.on{background:var(--accent);color:#04222e;border-color:var(--accent);font-weight:700}
  .time-bar input[type=range]{flex:1 1 auto;min-width:80px;accent-color:var(--accent)}
  .time-bar b{font-family:var(--mono);font-size:11.5px;color:var(--ink);flex:0 0 auto;
    min-width:104px;text-align:right;font-variant-numeric:tabular-nums}
  .seg.inline.mini{flex:0 0 auto;border-radius:7px}
  .seg.inline.mini .sg{background:transparent;border:0;color:var(--muted);cursor:pointer;
    padding:5px 9px;font-size:11.5px;font-family:var(--sans)}
  .seg.inline.mini .sg.on{background:var(--accent);color:#04222e;font-weight:700}
  .seg.inline.mini .sg:not(.on):hover{color:var(--ink)}
  /* The emitter's world is exactly the preview area, at any zoom — pResize makes
     the renderer the size of this box and the world is that divided by the zoom.
     The frame says where that is: it is the line wrap and bounce happen on. */
  #phost::after{content:"";position:absolute;inset:0;z-index:3;pointer-events:none;
    border:1px dashed rgba(94,208,255,.16)}
  #phost canvas{max-width:100%;max-height:100%}
  /* Where the attractor sits. An overlay, not a sprite — a sprite would smear
     into the trail buffer along with the particles. */
  .attr-mark{position:absolute;pointer-events:none;z-index:4;border-radius:50%;
    border:1px dashed rgba(94,208,255,.45);transform:translate(-50%,-50%);
    width:26px;height:26px;box-shadow:0 0 0 1px rgba(0,0,0,.35) inset}
  .attr-mark::after{content:"";position:absolute;left:50%;top:50%;width:9px;height:9px;margin:-4.5px 0 0 -4.5px;
    border-radius:50%;background:#5ed0ff;box-shadow:0 0 9px rgba(94,208,255,.85)}
  .attr-mark.placing{border-color:rgba(94,208,255,.9);animation:attrPulse 1.1s ease-in-out infinite}
  @keyframes attrPulse{50%{opacity:.35}}
  #phost.placing{cursor:crosshair}
  .btn.on{border-color:var(--accent);color:var(--accent)}
  /* one switch reveals every advanced row at once */
  #particlesPage:not(.adv) .adv{display:none}

  /* ---- the left panel -------------------------------------------------------
     Seven identical grey bars told you nothing about where an effect actually
     lives, and the way in — the preset library — looked like the least important
     line on the page. Three things fix that: a front door you can see, a search
     box so you never have to remember which section a control is in, and section
     headings that stick while you scroll so you always know where you are.
     Sections stay independently open: NN/G is clear that auto-collapsing one to
     open another stops you comparing two, and tuning an effect is comparing. */

  /* Search. Sticky so the way out of a long panel is never scrolled away. */
  .pfind{position:sticky;top:0;z-index:6;display:flex;align-items:center;gap:9px;
    padding:9px 13px;background:var(--panel);border-bottom:1px solid var(--line)}
  .pfind input[type=search]{flex:1;min-width:0}
  .pfind .advtog{display:flex;align-items:center;gap:6px;flex:0 0 auto;cursor:pointer;
    font-size:11px;font-weight:700;letter-spacing:.5px;text-transform:uppercase;color:var(--muted-2)}
  .pfind .advtog:hover{color:var(--muted)}
  .pfind .advtog input{accent-color:var(--accent);margin:0}
  .pfind-none{display:none;padding:14px 13px;color:var(--muted-2);font-size:12.5px;font-style:italic}
  #particlesPage.finding .pfind-none:not([hidden]){display:block}

  /* One section at a time, chosen from a strip.

     Seven stacked accordions in a 400px column never worked: the second one you
     opened was already below the fold, every heading looked like every other one,
     and "which section am I in" was a question you had to keep asking. A strip
     answers it before you scroll — it is the map, and the panel is one place.

     NN/G's warning about accordions is that closing one to open another stops you
     comparing two. That holds when the page IS the content; here the comparison
     happens in the preview, and the search box cuts across every section anyway,
     which is the one case tabs are genuinely bad at. */
  .psec-bar{position:sticky;top:var(--pfind-h,53px);z-index:5;display:flex;align-items:center;
    gap:4px;background:var(--panel);border-bottom:1px solid var(--line);padding-right:9px}
  .psec-tabs{flex:1;min-width:0;padding:9px 13px;border-bottom:0}
  .psec-tabs button{padding:4px 11px;font-size:11.5px}
  /* A section doing nothing says so on the strip, so you can see what a preset
     actually uses without opening anything. */
  .psec-tabs button.off{opacity:.55}
  .psec-tabs button.off::after{content:"off";margin-left:6px;font-size:9px;letter-spacing:.4px;opacity:.8}
  .psec-tabs button.active.off{opacity:1}
  .psec-help{flex:0 0 auto}
  .psec-help[hidden]{display:none}

  #particlesPage .block.psec{display:none;border-bottom:0}
  #particlesPage .block.psec.on{display:block}
  /* The strip already names the section, so its heading is one label too many —
     except while searching, when hits come from everywhere and the heading is the
     only thing saying where each one lives. */
  #particlesPage .block.psec>h3{display:none}
  #particlesPage .block.psec>h3 .fold-caret,
  #particlesPage .block.psec>h3 .fold-sum{display:none}
  #particlesPage.finding .psec-bar{display:none}
  #particlesPage.finding .block.psec{display:block}
  #particlesPage.finding .block.psec>h3{display:flex;position:sticky;
    top:var(--pfind-h,53px);z-index:4;background:var(--panel);color:var(--ink);
    padding:11px 13px 9px;margin:-12px -13px 8px;box-shadow:0 1px 0 var(--line)}

  /* While searching, the query decides what is open, and an advanced row that
     matches is shown — you asked for it by name. */
  #particlesPage.finding .ctl:not(.hit){display:none}
  #particlesPage.finding .ctl.hit{display:block}
  /* pFindRun marks anything with no hit under it, so a matching section shows the
     matching rows and not its grids, swatches and buttons as well. */
  #particlesPage.finding .nomatch{display:none}
  #pAdvLab{color:var(--muted-2);font-size:11.5px}
  #pDrawWrap{margin-top:8px}
  /* The pad's backing store is 240x180 and it draws in 0..1 coordinates, so a box
     of another shape stretches whatever you draw — and the stretch changed every
     time the divider moved. Same ratio as the canvas, capped so a wide panel does
     not turn it into a wall. */
  .drawpad{width:100%;max-width:320px;aspect-ratio:4/3;background:#0c1017;
    border:1px solid var(--line);border-radius:8px;display:block;cursor:crosshair;touch-action:none}
  .drawpad:hover{border-color:var(--accent)}
  .drawrow{display:flex;gap:8px;align-items:center;margin-top:8px}
  #pDrawMode button{padding:5px 8px;font-size:11.5px}
  /* custom modal */
