/* The shared grid and its Kanban, copied from reference/demo.html: the Kanban (lines 235–244) and the grid, its
   filter pane, tabs, frozen columns, header filters, cell editor and dropdown portal (416–529). Rules app/css already
   had from the demo are not repeated: --fz (406) and the .hl labels are in components.css, #view.ro (435–437) too.
   The additions this port needs follow at the end. */

/* ---------- kanban (demo 235–244) ---------- */
.kb{display:flex;gap:12px;overflow-x:auto;padding:2px 2px 8px;align-items:flex-start}
.kcol{flex:0 0 250px;padding:10px;min-height:120px;display:flex;flex-direction:column;gap:8px}
.kcol.over{background:rgba(47,155,214,.12)}
.kcol .kh{display:flex;align-items:center;gap:6px;padding:2px 4px 6px}
.kcol .kh .ct{margin-left:auto;font-size:10.5px;color:var(--muted)}
.kcard{background:var(--white);border:1px solid var(--edge);border-radius:12px;padding:9px 10px;cursor:grab;font-size:12px;display:flex;flex-direction:column;gap:4px}
.kcard:active{cursor:grabbing}
.kcard b{font-size:12.5px}
.kcard .m{color:var(--muted);font-size:11px;display:flex;gap:6px;flex-wrap:wrap}
.kcard.dragging{opacity:.4}

/* ---------- grid (demo 416–529) ---------- */
.qtwrap{display:flex;gap:12px;align-items:flex-start;min-width:0}
.fpane{flex:0 0 200px;padding:10px 10px 12px;max-height:calc(100vh - 212px);overflow-y:auto;overflow-x:hidden}
.fpane .fgrp{margin-bottom:10px}
.fpane .fgrp>.lbl{display:flex;align-items:center;gap:5px;cursor:pointer;user-select:none;padding:3px 4px;border-radius:6px}
.fpane .fgrp>.lbl:hover{background:var(--white);color:var(--ink)}
.fpane .fgrp>.lbl .cv{font-size:9px;transition:transform .16s;flex:0 0 auto}
.fpane .fgrp>.lbl .on{margin-left:auto;background:rgba(47,155,214,.18);color:#155f96;border-radius:999px;padding:0 5px;font-size:9px;letter-spacing:0}
.fpane .fgrp.shut>.lbl .cv{transform:rotate(-90deg)}
.fpane .fgrp.shut>.fopts{display:none}
.fpane .fopts{max-height:210px;overflow-y:auto;overflow-x:hidden}
.fopt{display:flex;align-items:center;gap:7px;font-size:11.5px;padding:3px 5px;border-radius:7px;cursor:pointer;line-height:1.3}
.fopt:hover{background:var(--white)}
.fopt input{flex:0 0 auto}
.fopt .nm{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.fopt .ct{margin-left:auto;color:var(--muted);font-size:10.5px;font-variant-numeric:tabular-nums;flex:0 0 auto}
.fopt.z{opacity:.42}
.fpane .clr{font-size:11.5px;color:var(--accentink);background:none;border:none;padding:4px 4px 0}
.qtcard{flex:1;min-width:0}
table.qt td{height:64px}
/* ---- Quote Tracker grid (mirrors the live tool) ---- */
.qtcard{flex:1;min-width:0;display:flex;flex-direction:column}
.qtscroll{overflow:auto;max-height:calc(100vh - 205px);outline:none;border-radius:16px 16px 0 0}
.qtscroll:focus-visible{box-shadow:inset 0 0 0 2px var(--ring)}
table.qtg{border-collapse:separate;border-spacing:0;table-layout:fixed;width:max-content;min-width:100%;font-size:12px}
table.qtg th,table.qtg td{border-right:1px solid var(--grid);border-bottom:1px solid var(--grid);padding:0;background:var(--cell);text-align:center;vertical-align:middle;overflow:hidden;white-space:nowrap;text-overflow:ellipsis}
table.qtg td{height:var(--rh);position:relative;cursor:cell}
table.qtg td.l{text-align:left}
table.qtg td .qcv{padding:0 8px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;line-height:1.3}
table.qtg td.wrapc .qcv{white-space:normal;max-height:var(--rh);display:-webkit-box;-webkit-line-clamp:3;-webkit-box-orient:vertical}
.dtabs{display:flex;gap:4px;margin:-4px 0 0;flex-wrap:wrap}
.dtab{padding:7px 14px;border-radius:10px 10px 0 0;border:1px solid var(--edge);border-bottom:none;background:rgba(255,255,255,.35);font-size:12.5px;font-weight:600;color:var(--muted);display:flex;align-items:center;gap:6px}
.dtab.on{background:var(--white);color:var(--ink);box-shadow:0 -2px 0 var(--accentink) inset}
.dtab .ct{font-size:10px;background:rgba(95,108,125,.12);border-radius:999px;padding:0 6px}
.dtabs+.qtwrap .qtscroll,.dtabs+.qtwrap .fpane{max-height:calc(100vh - 252px)}
table.qtg thead th{position:sticky;z-index:5;background:var(--thead);font-weight:700;color:var(--ink)}
table.qtg thead tr.letters th{top:0;height:18px;font-size:10px;color:var(--muted);letter-spacing:.06em;cursor:pointer;background:rgba(228,235,244,.99)}
table.qtg thead tr.hdr th{top:19px;height:40px;font-size:11.5px}
table.qtg thead tr.grps th{top:0;height:20px;font-size:10px;letter-spacing:.12em;text-transform:uppercase;color:var(--muted);background:rgba(228,235,244,.99)}
table.qtg thead tr.grps th.grp.nyc{color:#155f96}table.qtg thead tr.grps th.grp.hk{color:#7a4fc0}
table.qtg.hasgrp thead tr.letters th{top:21px}table.qtg.hasgrp thead tr.hdr th{top:40px}
table.qtg td.hkcol{background:rgba(148,102,214,.05)}table.qtg thead tr.hdr th.hkcol{color:#7a4fc0}
th.hc .hl.wraph{white-space:normal;line-height:1.1;font-size:10.5px;display:block}
table.qtg thead tr.hdr th.on{color:var(--accentink)}
table.qtg th.chk,table.qtg td.chk{position:sticky;left:0;z-index:4;background:var(--fz);width:34px}
table.qtg th.rn,table.qtg td.rn{position:sticky;left:34px;z-index:4;background:var(--fz);width:44px;font-size:10.5px;color:var(--muted);cursor:pointer;font-variant-numeric:tabular-nums}
table.qtg td.rn:hover{background:var(--fzh);color:var(--ink)}
table.qtg th.fzc,table.qtg td.fzc{position:sticky;z-index:3}
table.qtg td.fzc{background:var(--fz)}
table.qtg th.fzl,table.qtg td.fzl{box-shadow:6px 0 10px -6px rgba(31,45,75,.28)}
table.qtg thead th.fzc{z-index:7}table.qtg thead th.chk,table.qtg thead th.rn{z-index:8}
table.qtg td.insel{background:rgba(47,155,214,.13)}
table.qtg td.fzc.insel{background:#d6e6f5}
table.qtg td.cur{box-shadow:inset 0 0 0 2px var(--accentink);z-index:2}
table.qtg tr.tk td.chk,table.qtg tr.tk td.rn{background:rgba(148,102,214,.18)}
table.qtg tr.rsel td.rn{background:rgba(47,155,214,.25);color:var(--ink)}
table.qtg tr.fill td{background:rgba(255,255,255,.35);cursor:cell;height:var(--rh)}
table.qtg tr.fill td:hover{background:rgba(47,155,214,.08)}
.qtimg{height:var(--ih);max-width:118px;width:auto;object-fit:contain;display:inline-block;cursor:zoom-in;vertical-align:middle;border-radius:4px}
.qtimg.ph{width:var(--ih);background:repeating-linear-gradient(45deg,rgba(95,108,125,.08),rgba(95,108,125,.08) 6px,transparent 6px,transparent 12px);cursor:pointer}
.qpill{display:inline-block;padding:2px 8px;border-radius:999px;font-size:11px;font-weight:700;margin:1px 2px;white-space:nowrap;line-height:1.35}
.sarr{position:absolute;left:6px;top:50%;transform:translateY(-50%);display:flex;flex-direction:column;line-height:1;cursor:pointer;opacity:.45}
.sarr i{font-style:normal;font-size:7px;color:var(--muted)}.sarr i.a{color:var(--accentink)}
th.hc:hover .sarr,th.hc.on .sarr{opacity:1}
table.qtg thead th.hc{position:sticky;padding:0 18px 0 22px}
.fcar{position:absolute;right:5px;top:50%;transform:translateY(-50%);font-size:9px;color:var(--muted);cursor:pointer;padding:3px;border-radius:4px;opacity:.55}
.fcar:hover,.fcar.act{opacity:1;color:var(--accentink);background:var(--hover)}
.hfilt{position:absolute;top:100%;left:0;z-index:40;background:var(--glass);backdrop-filter:blur(18px);border:1px solid var(--edge);border-radius:12px;box-shadow:0 12px 32px rgba(31,45,75,.18);padding:10px;min-width:220px;text-align:left;white-space:normal;font-weight:400}
.hft{font-size:10px;font-weight:700;letter-spacing:.08em;text-transform:uppercase;color:var(--muted);margin-bottom:6px}
.ccar{position:absolute;right:3px;top:50%;transform:translateY(-50%);font-size:9px;color:var(--muted);cursor:pointer;padding:2px 4px;border-radius:4px;opacity:0}
table.qtg td:hover .ccar,table.qtg td.cur .ccar{opacity:.8}.ccar:hover{background:var(--hover);opacity:1}
.qtedit{position:absolute;inset:0;width:100%;height:100%;border:2px solid var(--accentink);border-radius:0;padding:0 8px;font-size:12px;background:var(--white);z-index:6;box-shadow:none}
.qtedit:focus{box-shadow:none;border-color:var(--accentink)}
table.qtg td.editing{overflow:visible;z-index:20}
.qtdd.portal,.hfilt.portal{position:fixed;left:auto;top:auto;z-index:400;border-radius:10px}
.qtdd.portal.up{border-radius:10px 10px 0 0}
.qtdd.portal{max-height:min(320px,50vh)}
.qtdd{position:absolute;top:100%;left:0;z-index:50;min-width:100%;background:var(--glass);backdrop-filter:blur(18px);border:1px solid var(--edge);border-radius:0 0 10px 10px;box-shadow:0 12px 32px rgba(31,45,75,.2);max-height:240px;overflow-y:auto;text-align:left;white-space:nowrap}
.qopt{padding:5px 10px;cursor:pointer;display:flex;align-items:center;gap:6px;font-size:12px}
.qopt:hover,.qopt.hi{background:var(--hover)}.qopt.on{background:rgba(47,155,214,.1)}.qopt.dim{color:var(--muted);cursor:default}.qopt.clr{color:var(--muted);border-top:1px solid var(--grid)}
.ck2{width:14px;display:inline-block;color:var(--good);font-weight:700}
.qdhint{font-size:10px;color:var(--muted);padding:4px 10px;border-top:1px solid var(--grid)}
.qgo{border:none;background:none;color:var(--accentink);font-size:12px;cursor:pointer;padding:0 3px;opacity:0}
table.qtg td:hover .qgo{opacity:1}.qgo.dim{color:var(--muted);opacity:.3}
.qtstatus{display:flex;align-items:center;gap:8px;padding:6px 12px;border-top:1px solid var(--grid);font-size:11.5px;color:var(--muted);background:rgba(228,235,244,.6);border-radius:0 0 16px 16px;white-space:nowrap;overflow:hidden}
.qtstatus b{color:var(--ink)}.qtstatus .ok{color:var(--good);font-weight:600;margin-left:6px}.qtstatus .sp{flex:1}.qtstatus .hint{overflow:hidden;text-overflow:ellipsis}
.fpane .fhd{font-size:12.5px;font-weight:700;padding:2px 4px 8px;display:flex;align-items:center}
.fpane.big{flex:0 0 250px;font-size:13px}
.fpane.big .fgrp>.lbl{font-size:12px;padding:6px 6px}
.fpane.big .fopt{font-size:12.5px;padding:5px 6px}
.fpane.big .fopt .ct{font-size:11.5px}
.fpane.big .fopts{max-height:280px}
.pcol{margin-left:auto;border:1px solid var(--edge);background:var(--white);border-radius:7px;width:24px;height:24px;font-size:14px;color:var(--muted);cursor:pointer;line-height:1}
.pcol:hover{color:var(--accentink)}
.fpane.mini{flex:0 0 34px;padding:10px 0;display:flex;flex-direction:column;align-items:center;gap:8px;cursor:pointer;font-size:13px;color:var(--muted)}
.fpane.mini:hover{color:var(--accentink)}
.fpane.mini .vlab{writing-mode:vertical-rl;font-size:11px;font-weight:700;letter-spacing:.1em;text-transform:uppercase}
.qlink{color:var(--accentink);font-weight:700;cursor:pointer;text-decoration:none}
a.trk{color:var(--accentink);font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:11.5px;text-decoration:none;font-weight:600}
a.trk:hover{text-decoration:underline}
.qlink.dimlink{font-weight:500;opacity:.7}
table.qtg td.c-supplier.fzc,table.qtg td.c-desc.fzc{background:var(--fz)}
table.qtg td.rocol{background:rgba(95,108,125,.05);color:var(--muted)}

/* ---------- additions for the port ---------- */
/* The grid places its rows itself (only the rows in view are drawn), so the browser never shifts the pane for them. */
.qtscroll{overflow-anchor:none}
/* A cell's text never widens its column, so the widths are DT_COLS's (each header still fits its title, as in the
   demo) and don't change as other rows are drawn while scrolling. Longer text ends in "…"; the drawer shows it all. */
table.qtg td .qcv{contain:inline-size}
/* The Kanban draws only the cards in view (Q30): each column scrolls inside itself and its cards have one height. */
.kb.virt{min-width:0}
.kb.virt .kbody{overflow-y:auto;overscroll-behavior:contain;max-height:calc(100vh - 300px);min-height:48px;margin:0 -4px;padding:0 4px}
.kb.virt .kcard{height:var(--kch);margin-bottom:var(--kgap);overflow:hidden}
.kb.virt .kcard b,.kb.virt .kcard .m{white-space:nowrap;overflow:hidden;text-overflow:ellipsis;flex-wrap:nowrap}
.kb.virt .kcard .m>span{flex:0 0 auto}
.kspace{flex:0 0 auto}
.kb.virt .kcard:not([draggable]){cursor:pointer}
/* A selected range shows on the frozen Supplier column too (the demo's c-supplier rule above hid it there). */
table.qtg tr td.fzc.insel{background:#d6e6f5}
/* A header filter with many values scrolls inside its popup (the demo's list ran past the popup's edge). */
.hfilt .fopts{overflow-y:auto;overflow-x:hidden}
/* The drawer's pills (demo 363) and the grid's read-only marker where the demo shows "All changes saved". */
.pillrow{display:flex;gap:6px;flex-wrap:wrap}
.qtstatus .rotag{font-weight:600;margin-left:6px;color:var(--warn)}
#drawer .dbd .hint{margin-top:-4px}
/* A read-only drawer looks like the demo's read-only fields (#view.ro, 435): faded, no fill, nothing to click. */
#drawer .dbd.ro input,#drawer .dbd.ro select,#drawer .dbd.ro textarea{pointer-events:none;opacity:.75;color:var(--ink);background:transparent}
/* Step 3, editing. A new row typed before its Supplier is held on the page, marked, until it can be saved (plan
   2.8.3); the Supplier cell says why. Refused cells of a paste are listed in their popup (Q31). */
table.qtg tr.held td{background:rgba(224,169,46,.10)}
table.qtg tr.held td.fzc{background:#f7efd9}
table.qtg tr.held td.rn{box-shadow:inset 3px 0 0 var(--warn)}
.qnote{color:var(--warn);font-weight:600;font-size:11px;white-space:nowrap}
.dtprob{margin:4px 0 0;padding-left:18px;max-height:46vh;overflow-y:auto;font-size:12.5px;line-height:1.5}
.dtprob li{margin:2px 0}
.qtstatus .rotag.held{color:var(--warn)}
/* A cell editor whose save was refused because someone else saved the row first keeps the typing, marked, and shows
   theirs just under the cell (autosave.js's note, which would otherwise sit under the editor). */
table.qtg td.editing .cfl{position:absolute;left:0;top:100%;z-index:7;background:var(--white);border:1px solid var(--warn);border-radius:0 0 6px 6px;padding:1px 6px;line-height:1.5}
/* Step 4. The drawer's PO# line (Q20): the order it links to, with its customer and date, so a wrong PO stands out;
   a six-digit match is the dimmer link, as in the grid. New Sample's notes beside a field, and → Order Tracker's units. */
.poline{font-size:12.5px;margin:0 0 12px;padding:8px 10px;border:1px solid var(--grid);border-radius:10px;background:var(--white)}
.poline.dim .qlink{font-weight:500;opacity:.8}
.poline ul{margin:6px 0 0;padding-left:18px}
.poline li{margin:2px 0}
.form .fld span .dtnh{display:inline;font-style:normal;text-transform:none;letter-spacing:0;font-weight:500;margin-left:4px}
.plines{display:flex;flex-direction:column;gap:6px;max-height:46vh;overflow-y:auto}
.pline{justify-content:space-between;padding:4px 0;border-bottom:1px solid var(--grid)}
.pline .pstyle{min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.pline input{flex:0 0 110px;width:110px;text-align:right}
.pline input.bad{border-color:var(--bad);background:var(--bad-soft)}
