/* A class that sets `display` outruns the `hidden` attribute, which the browser
   applies at user-agent specificity. Without this, hiding the key row or the
   user menu by attribute silently does nothing. */
[hidden]{display:none!important}

:root{
  --bg:#faf9f7; --panel:#fff; --ink:#1c1a17; --muted:#6b6560; --line:#e4dfd8;
  --orange:#FF7E05; --green:#2E7D32; --amber:#F0A500; --red:#C62828;
}
@media (prefers-color-scheme: dark){
  :root{ --bg:#171614; --panel:#201e1b; --ink:#f0ece6; --muted:#a09890; --line:#332f2a; }
}
*{box-sizing:border-box}
body{margin:0;background:var(--bg);color:var(--ink);
  font:15px/1.55 -apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,sans-serif}
header{padding:18px 28px;border-bottom:1px solid var(--line);display:flex;align-items:flex-start;gap:20px;flex-wrap:wrap}
.brand{font-size:21px;font-weight:560;letter-spacing:-.01em}
.brand .mark{
  /* The logo as a CSS mask rather than an <img>: an external SVG cannot inherit
     the page colour, a mask can. One file then serves the header, any future
     dark or inverted placement, and a hover state -- with no second copy of the
     artwork to drift out of sync with the brand original. */
  display:inline-block;width:1.15em;height:1.15em;vertical-align:-.19em;
  margin-right:.14em;background-color:var(--orange);
  -webkit-mask:url("/static/muldek-mark.svg") center/contain no-repeat;
  mask:url("/static/muldek-mark.svg") center/contain no-repeat;
}
.brand .thin{font-weight:400;color:var(--muted)}
.tag{color:var(--muted);margin-top:5px;font-size:14px}
main{max-width:none;margin:0;padding:24px 28px 70px}

.dropzone{border:2px dashed var(--line);border-radius:14px;background:var(--panel);
  padding:56px 24px;text-align:center;transition:.15s;cursor:pointer}
.dropzone:hover,.dropzone.over{border-color:var(--orange);background:rgba(255,126,5,.05)}
.dz-icon{font-size:38px;color:var(--orange);line-height:1}
.dropzone h1{font-size:22px;margin:14px 0 6px;font-weight:600}
.dropzone p{color:var(--muted);margin:0 0 4px}
.dz-buttons{margin:20px 0 6px;display:flex;gap:10px;justify-content:center}
.fine{font-size:12.5px;color:var(--muted);max-width:460px;margin:14px auto 0}

button{font:inherit;font-weight:550;padding:9px 17px;border-radius:8px;cursor:pointer;
  border:1px solid var(--orange);background:var(--orange);color:#fff}
button:hover{filter:brightness(1.07)}
button.ghost{background:transparent;color:var(--ink);border-color:var(--line)}

.panel{background:var(--panel);border:1px solid var(--line);border-radius:12px;padding:18px 20px;margin-top:20px}
.prow{display:flex;gap:14px;align-items:baseline;flex-wrap:wrap}
.muted{color:var(--muted)}
.code{margin-left:auto;font-family:ui-monospace,SFMono-Regular,Menlo,monospace;
  font-size:12.5px;color:var(--muted);border:1px solid var(--line);padding:2px 8px;border-radius:5px}
.bar{height:6px;background:var(--line);border-radius:4px;margin-top:12px;overflow:hidden}
.bar-fill{height:100%;width:0;background:var(--orange);transition:width .25s}


.stat{background:var(--panel);border:1px solid var(--line);border-radius:10px;padding:13px 15px}
.stat .v{font-size:23px;font-weight:650;letter-spacing:-.02em}
.stat .k{font-size:12px;color:var(--muted);margin-top:2px}

.notice{border-radius:9px;padding:11px 14px;margin-bottom:10px;font-size:13.5px;border:1px solid}
.notice.warn{border-color:var(--amber);background:rgba(240,165,0,.09)}
.notice.err{border-color:var(--red);background:rgba(198,40,40,.08)}

.tablewrap{overflow-x:auto;border:1px solid var(--line);border-radius:12px;background:var(--panel)}
table{border-collapse:collapse;width:100%;font-size:13.5px;min-width:840px}
th,td{padding:10px 13px;text-align:left;border-bottom:1px solid var(--line);white-space:nowrap}
th{font-size:11.5px;text-transform:uppercase;letter-spacing:.05em;color:var(--muted);font-weight:600}
tbody tr:last-child td{border-bottom:none}
td.n,th.n{text-align:right;font-variant-numeric:tabular-nums}
.pill{display:inline-block;padding:2px 9px;border-radius:20px;font-size:11.5px;font-weight:600}
.SHEET_METAL{background:rgba(46,125,50,.15);color:var(--green)}
.FLAT_PLATE{background:rgba(59,130,246,.15);color:#3B82F6}
.PRISMATIC{background:rgba(240,165,0,.16);color:var(--amber)}
.TURNED{background:rgba(155,89,182,.16);color:#9B59B6}
.MACHINED{background:rgba(107,101,96,.16);color:var(--muted)}
.conf{font-variant-numeric:tabular-nums;font-size:12.5px}
.conf.low{color:var(--amber);font-weight:600}
.actions{display:flex;gap:16px;align-items:center;margin-top:18px}
/* Authoritative: .hidden is applied by script all over the app, and any rule
   defined LATER with equal specificity - .jobbar{display:flex}, for instance -
   would otherwise silently beat it. The class was being applied correctly and
   doing nothing, which reads as a logic bug and is not one. */
.hidden{display:none !important}

.keyrow{display:flex;gap:9px;align-items:center;margin-top:10px;font-size:13px}
.keyrow label{color:var(--muted)}
.keyrow input{font:inherit;padding:5px 9px;border-radius:7px;border:1px solid var(--line);
  background:var(--panel);color:var(--ink);width:210px}
td.thumb{width:104px;padding:5px 8px}
td.thumb img{display:block;width:96px;height:60px;object-fit:contain;
  background:rgba(127,127,127,.07);border-radius:6px}
td.edit select,td.edit input{font:inherit;font-size:13px;padding:4px 7px;border-radius:6px;
  border:1px solid var(--line);background:var(--panel);color:var(--ink);max-width:150px}
td.edit input{width:74px;text-align:right}
.ovr{outline:2px solid var(--orange);outline-offset:-2px}
.badge-ovr{display:inline-block;margin-left:5px;font-size:10px;color:var(--orange);font-weight:700}
tr.review{background:rgba(240,165,0,.06)}
.saving{opacity:.5}

/* --- M02 bill-of-materials tree --- */
.tree{margin-top:12px;font-size:13.5px}
.tnode{display:flex;align-items:center;gap:9px;padding:5px 8px;border-radius:7px;
  border:1px solid transparent;cursor:grab}
.tnode:hover{background:rgba(127,127,127,.07);border-color:var(--line)}
.tnode.drop{border-color:var(--orange);background:rgba(255,126,5,.12)}
.tnode.dragging{opacity:.4}
.tkind{font-size:10.5px;text-transform:uppercase;letter-spacing:.05em;padding:1px 7px;
  border-radius:20px;font-weight:700}
.tkind.assembly{background:rgba(255,126,5,.18);color:var(--orange)}
.tkind.part{background:rgba(59,130,246,.15);color:#3B82F6}
.tkind.manual{background:rgba(240,165,0,.18);color:var(--amber)}
.tlabel{font-weight:550}
.tqty{font-variant-numeric:tabular-nums;color:var(--muted)}
.tqty input{width:58px;text-align:right;font:inherit;padding:2px 6px;border-radius:5px;
  border:1px solid var(--line);background:var(--panel);color:var(--ink)}
.tacts{margin-left:auto;display:flex;gap:5px;opacity:0;transition:.12s}
.tnode:hover .tacts{opacity:1}
.tacts button{font-size:11px;padding:3px 9px;border-radius:6px;background:transparent;
  color:var(--muted);border:1px solid var(--line);font-weight:500}
.tacts button:hover{color:var(--ink);border-color:var(--orange)}
.tchildren{margin-left:22px;border-left:1px dashed var(--line);padding-left:10px}

/* --- M03 routing editor --- */
.opsbtn{background:transparent;border:1px solid transparent;color:var(--ink);
  font:inherit;font-variant-numeric:tabular-nums;padding:2px 8px;border-radius:6px}
.opsbtn:hover{border-color:var(--orange);color:var(--orange)}
.opsmodal{position:fixed;inset:0;background:rgba(0,0,0,.55);display:flex;
  align-items:flex-start;justify-content:center;padding:6vh 16px;z-index:50;overflow:auto}
.opsinner{background:var(--panel);border:1px solid var(--line);border-radius:14px;
  padding:20px 22px;max-width:1000px;width:100%}
.opstable{width:100%;border-collapse:collapse;margin-top:14px;font-size:13px}
.opstable th{font-size:11px;text-transform:uppercase;letter-spacing:.05em;color:var(--muted);
  text-align:left;padding:6px 8px;border-bottom:1px solid var(--line)}
.opstable td{padding:5px 8px;border-bottom:1px solid var(--line)}
.opstable input{font:inherit;padding:4px 7px;border-radius:6px;border:1px solid var(--line);
  background:var(--bg);color:var(--ink);width:100%}
.opstable td.n input{text-align:right;width:82px}
.opsinner select{font:inherit;padding:5px 9px;border-radius:7px;border:1px solid var(--line);
  background:var(--panel);color:var(--ink)}

/* --- job code badge: the handle you paste into Odoo, so it must not vanish
       when the progress panel does --- */
.jobbadge{display:flex;align-items:center;gap:9px;
  font:inherit;font-size:13px;padding:7px 13px;border-radius:9px;cursor:pointer;
  background:var(--panel);border:1px solid var(--line);color:var(--ink)}
.jobbadge:hover{border-color:var(--orange)}
.jobbadge .jb-label{color:var(--muted);font-size:11px;text-transform:uppercase;letter-spacing:.06em}
.jobbadge .jb-code{font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-weight:650;
  letter-spacing:.04em}
.jobbadge .jb-hint{font-size:11px;color:var(--muted);border-left:1px solid var(--line);
  padding-left:9px}
.jobbadge.copied{border-color:var(--green)}
.jobbadge.copied .jb-hint{color:var(--green);font-weight:650}

/* --- attention tiles: same shape as the fact tiles so the row stays one
       dashboard, but tinted so a problem is obvious at a glance --- */
.stat.warn{border-color:var(--amber);background:rgba(240,165,0,.08)}
.stat.warn .v{color:var(--amber)}
.stat.info{border-color:rgba(59,130,246,.45);background:rgba(59,130,246,.07)}
.stat.info .v{color:#3B82F6}
.stat[title]{cursor:help}

/* --- once a quote is open the drop zone is clutter; this replaces it --- */
.jobbar{display:flex;align-items:center;gap:12px;padding:12px 18px;border-radius:11px;
  border:1px solid var(--line);background:var(--panel);font-size:13.5px}
.jobbar button{font-size:13px;padding:7px 14px}
.dropzone.appending{border-color:var(--orange)}
.dropzone.appending h1::after{content:" — adding to this quote";font-weight:400;color:var(--orange)}

/* --- hover preview: the tree is names and numbers, a picture is how a part is
       actually recognised --- */
.hoverpreview{position:fixed;z-index:60;pointer-events:none;opacity:0;
  transform:translateY(-4px);transition:opacity .12s,transform .12s;
  background:var(--panel);border:1px solid var(--line);border-radius:11px;
  padding:8px;box-shadow:0 10px 28px rgba(0,0,0,.4);width:216px}
.hoverpreview.shown{opacity:1;transform:translateY(0)}
.hoverpreview img{display:block;width:200px;height:132px;object-fit:contain;
  background:rgba(127,127,127,.07);border-radius:7px}
.hoverpreview.noimg img{display:none}
.hoverpreview.noimg::before{content:"no preview";display:block;height:132px;
  line-height:132px;text-align:center;color:var(--muted);font-size:12px;
  background:rgba(127,127,127,.07);border-radius:7px}
.hoverpreview .hp-label{margin-top:6px;font-size:12px;color:var(--muted);
  text-align:center;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}

/* --- place-an-existing-part picker --- */
.pplist{margin-top:14px;display:grid;grid-template-columns:1fr 1fr;gap:8px;max-height:52vh;overflow:auto}
.pprow{display:flex;align-items:center;gap:11px;padding:8px 10px;border-radius:9px;
  border:1px solid var(--line);background:var(--bg);color:var(--ink);font:inherit;
  font-size:13px;text-align:left;cursor:pointer}
.pprow:hover{border-color:var(--orange)}
.pprow img{width:58px;height:38px;object-fit:contain;background:rgba(127,127,127,.07);
  border-radius:5px;flex:none}
.pprow .pplabel{font-weight:550}
.pprow .muted{margin-left:auto;font-size:11px}

/* --- 3D preview + settings ------------------------------------------------ */
.small{font-size:12px;padding:4px 10px}
.plabel{cursor:pointer;text-decoration:underline;text-decoration-style:dotted;
  text-underline-offset:3px;text-decoration-color:var(--muted)}
.plabel:hover{text-decoration-color:var(--fg)}
.viewwrap{height:min(62vh,560px);background:#0e1116;border:1px solid var(--line);
  border-radius:10px;margin-top:12px;position:relative}
.viewwrap model-viewer{width:100%;height:100%;background-color:#0e1116;--poster-color:transparent}
.viewnote{padding:26px 22px;text-align:center;color:var(--muted);font-size:13px;line-height:1.6}
.viewnote strong{color:var(--fg);display:block;margin-bottom:6px;font-size:14px}
.viewnote button{margin-top:14px}
.setrow{display:flex;gap:12px;align-items:flex-start;padding:12px 0;border-bottom:1px solid var(--line)}
.setrow:last-of-type{border-bottom:0}
.setrow input[type=checkbox]{margin-top:3px;width:16px;height:16px}
.setrow .sd{font-size:12.5px;color:var(--muted);line-height:1.55;margin-top:3px}
.setrow select,.setrow input[type=number]{font:inherit;padding:4px 7px;border-radius:6px;
  border:1px solid var(--line);background:var(--bg);color:var(--fg)}
.warnbox{background:rgba(220,120,40,.10);border:1px solid rgba(220,120,40,.35);
  border-radius:8px;padding:10px 12px;font-size:12.5px;line-height:1.55;margin-top:12px}

/* Per-quote retention toggle + toast */
.qretain{display:flex;align-items:center;gap:7px;font-size:12.5px;color:var(--muted);
  cursor:pointer;user-select:none}
.qretain input{width:15px;height:15px;cursor:pointer}
.qretain:hover{color:var(--ink)}
.toast{position:fixed;left:50%;bottom:26px;transform:translateX(-50%) translateY(12px);
  background:var(--panel);border:1px solid var(--line);border-radius:10px;
  padding:12px 16px;font-size:13px;max-width:440px;line-height:1.5;
  box-shadow:0 8px 28px rgba(0,0,0,.18);opacity:0;pointer-events:none;
  transition:.18s;z-index:80}
.toast.shown{opacity:1;transform:translateX(-50%) translateY(0)}


/* --- header: brand left, account right ------------------------------------ */
.brandblock{min-width:0}
.hdr-right{margin-left:auto;display:flex;align-items:center;gap:12px;flex-wrap:wrap;
  justify-content:flex-end}
.usermenu{position:relative}
.userchip{display:flex;align-items:center;gap:8px;background:transparent;color:var(--ink);
  border:1px solid var(--line);padding:5px 10px 5px 6px;border-radius:20px;font-size:13px;
  font-weight:500}
.userchip:hover{border-color:var(--muted);filter:none}
.uavatar{width:24px;height:24px;border-radius:50%;background:var(--orange);color:#fff;
  display:grid;place-items:center;font-size:11px;font-weight:700;letter-spacing:.02em}
.uname{max-width:190px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.ucaret{color:var(--muted);font-size:10px}
.userpop{position:absolute;right:0;top:calc(100% + 7px);min-width:220px;z-index:60;
  background:var(--panel);border:1px solid var(--line);border-radius:11px;padding:6px;
  box-shadow:0 10px 30px rgba(0,0,0,.16)}
.userpop .upop-head{padding:8px 10px 9px;border-bottom:1px solid var(--line);margin-bottom:5px;
  display:flex;flex-direction:column;gap:2px;font-size:12.5px;line-height:1.35}
.userpop .upop-head .muted{font-size:11.5px}
.userpop a,.userpop button{display:block;width:100%;text-align:left;background:transparent;
  border:0;color:var(--ink);font:inherit;font-size:13px;font-weight:500;padding:8px 10px;
  border-radius:7px;text-decoration:none;cursor:pointer}
.userpop a:hover,.userpop button:hover{background:rgba(128,128,128,.13);filter:none}

/* --- workspace: BOM left, routing right ----------------------------------- */
.workspace{display:grid;gap:18px;align-items:start;
  grid-template-columns:minmax(380px,5fr) minmax(460px,7fr)}
/* One column when the BOM is not on screen yet, and on a narrow window --
   two half-width panels on a laptop screen are worse than one usable one. */
.workspace:has(> #structure.hidden){grid-template-columns:1fr}
@media (max-width:1120px){.workspace{grid-template-columns:1fr}}
.workspace > .panel{margin:0;min-width:0}
#tree{max-height:60vh;overflow:auto}
.routing-empty{padding:26px 20px;text-align:center;color:var(--muted)}
.routing-empty strong{display:block;color:var(--ink);font-size:15px;margin-bottom:7px}
.routing-empty p{margin:0 auto;max-width:380px;font-size:13px;line-height:1.6}
.rt-scroll{max-height:52vh;overflow:auto;margin-top:12px}
.rt-head{display:flex;align-items:baseline;gap:10px;flex-wrap:wrap}
.rt-head .rt-name{font-weight:620;font-size:15px}
.rt-total{display:flex;gap:18px;align-items:baseline;margin-top:12px;padding-top:11px;
  border-top:1px solid var(--line);font-size:13px}
.rt-total b{font-size:15px}
.tnode.selected{background:rgba(255,126,5,.13);border-color:var(--orange)}
.tnode .tlabel{cursor:pointer}
.opstable input{width:100%}
.opstable td.n input{text-align:right}

/* A defect, not a gap: the other attention tiles are work still to do, a name
   clash is something already wrong. */
.stat.err{border-color:var(--red);background:rgba(198,40,40,.09)}
.stat.err .v{color:var(--red)}

/* --- form controls, one house style --------------------------------------
   Native selects render with the platform's own 1990s chrome, which looked
   nothing like the rest of this. Appearance is reset and the arrow is drawn as
   a background SVG so the control matches every other field. */
.sel,
select.inp,
.opstable select,
.member .m-role select,
.setrow select,
.inviterow select,
td.edit select,
#parts select{
  appearance:none;-webkit-appearance:none;-moz-appearance:none;
  font:inherit;font-size:13px;color:var(--ink);background-color:var(--bg);
  border:1px solid var(--line);border-radius:7px;padding:6px 28px 6px 10px;
  background-image:url("data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 8'%3E%3Cpath d='M1 1.5 6 6.5l5-5' fill='none' stroke='%236b6560' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
  background-repeat:no-repeat;background-position:right 9px center;background-size:11px;
  cursor:pointer;line-height:1.35}
.sel:hover,select.inp:hover,td.edit select:hover{border-color:var(--muted)}
.sel:focus,select.inp:focus,td.edit select:focus{outline:2px solid var(--orange);
  outline-offset:-1px;border-color:var(--orange)}
.sel:disabled{opacity:.45;cursor:not-allowed}

.inp{font:inherit;font-size:13px;color:var(--ink);background:var(--bg);
  border:1px solid var(--line);border-radius:7px;padding:6px 10px;width:100%}
.inp:focus{outline:2px solid var(--orange);outline-offset:-1px;border-color:var(--orange)}
.inp.n{text-align:right}

/* --- make / buy / subcontract --------------------------------------------- */
.segrow{display:flex;align-items:center;gap:14px;flex-wrap:wrap;margin:14px 0 4px}
.seg{display:inline-flex;border:1px solid var(--line);border-radius:9px;overflow:hidden}
.seg button{background:transparent;color:var(--muted);border:0;border-right:1px solid var(--line);
  border-radius:0;font-size:12.5px;font-weight:560;padding:7px 15px}
.seg button:last-child{border-right:0}
.seg button:hover{background:rgba(128,128,128,.10);color:var(--ink);filter:none}
.seg button.on{background:var(--orange);color:#fff}
.seg button.on:hover{background:var(--orange);filter:brightness(1.05)}

.fieldgrid{display:grid;grid-template-columns:minmax(140px,auto) minmax(0,1fr);
  gap:10px 14px;align-items:center;margin:16px 0 4px;max-width:560px}
.fl{font-size:12.5px;font-weight:560}
.hintbox{background:rgba(59,130,246,.09);border:1px solid rgba(59,130,246,.32);
  border-radius:8px;padding:9px 12px;font-size:12.5px;margin-top:14px}

/* --- start from: a template OR a similar part ----------------------------- */
.startfrom{border:1px solid var(--line);border-radius:10px;padding:6px 14px 12px;
  margin:16px 0 4px}
.startfrom legend{font-size:11px;text-transform:uppercase;letter-spacing:.06em;
  color:var(--muted);font-weight:700;padding:0 6px}
.orline{display:flex;align-items:center;gap:10px;padding:5px 0;font-size:13px;cursor:pointer}
.orline input[type=radio]{width:15px;height:15px;accent-color:var(--orange);cursor:pointer}
.orline > span{min-width:132px}
.orline.off{opacity:.55}
.ordiv-sm{font-size:11px;text-transform:uppercase;letter-spacing:.07em;color:var(--muted);
  margin:2px 0 2px 25px;font-weight:700}

/* --- ops rows: grab on the left, bin on the right ------------------------- */
.opstable td.drag,.opstable th.drag{width:26px;padding-left:2px;padding-right:2px;
  color:var(--muted);cursor:grab;text-align:center;vertical-align:middle}
.opstable tr:hover td.drag{color:var(--ink)}
.opstable tr.dragging{opacity:.4}
.opstable tr.dropbefore td{box-shadow:inset 0 2px 0 var(--orange)}
.opstable tr.dropafter td{box-shadow:inset 0 -2px 0 var(--orange)}
.opstable td.rowdel{width:34px;text-align:right}
.iconbtn{background:transparent;border:1px solid transparent;color:var(--muted);
  border-radius:6px;padding:4px 6px;line-height:0;cursor:pointer}
.iconbtn:hover{color:var(--red);border-color:rgba(198,40,40,.4);
  background:rgba(198,40,40,.08);filter:none}

/* --- selecting several parts --------------------------------------------- */
#parts th.pick,#parts td.pick{width:34px;padding-left:14px;padding-right:0}
#parts td.pick input,#parts th.pick input{width:15px;height:15px;accent-color:var(--orange);
  cursor:pointer}
#parts tr.selected{background:rgba(255,126,5,.07)}
.bulkbar{display:flex;align-items:center;gap:14px;flex-wrap:wrap;margin-bottom:12px;
  padding:10px 14px;border:1px solid var(--orange);border-radius:10px;
  background:rgba(255,126,5,.07);font-size:13px}
.bulkbar .seg button{font-size:12px;padding:5px 12px}

/* --- sourcing shown on the line ------------------------------------------- */
.dchip{display:inline-block;min-width:34px;text-align:center;font-size:9.5px;font-weight:800;
  letter-spacing:.06em;padding:2px 5px;border-radius:4px;flex:0 0 auto}
.dchip.make{background:rgba(46,125,50,.20);color:#5fb567}
/* Violet, not blue: blue already means "we guessed this" on the summary
   tiles, and two meanings for one colour on the same screen is one too many. */
.dchip.buy{background:rgba(150,105,225,.22);color:#b18cf0}
.dchip.subcontract{background:rgba(240,165,0,.20);color:var(--amber)}
.dchip.none{background:transparent}
.tnode{cursor:pointer}
.tnode:hover{background:rgba(128,128,128,.07)}
.tnode .tacts button[data-a="view3d"]{font-weight:700;letter-spacing:.03em}

/* the sourcing select carries its own colour, so the column reads at a glance */
#parts td.srccell select{font-weight:600;font-size:12px;padding:4px 24px 4px 8px}
#parts select.src-make{color:#5fb567;border-color:rgba(46,125,50,.45)}
#parts select.src-buy{color:#b18cf0;border-color:rgba(150,105,225,.48)}
#parts select.src-subcontract{color:var(--amber);border-color:rgba(240,165,0,.45)}

.pickbar{display:flex;align-items:center;gap:8px;flex-wrap:wrap;margin-bottom:10px;
  font-size:12.5px}
.pickby{display:flex;gap:6px;flex-wrap:wrap;padding-left:6px;border-left:1px solid var(--line)}
.pickby button{font-size:11.5px;padding:3px 9px;text-transform:capitalize}
.pickbar .ghost.small{padding:3px 10px;font-size:11.5px}

/* The 3D button stays put; the editing actions still appear on hover. Looking
   at a part is something you do while reading the list, not while editing it. */
/* Every row action appears on hover, the 3D button included -- a control that
   is always visible on every line is noise in a hundred-line tree. */
.tnode .tacts{opacity:0;transition:opacity .12s}
.tnode:hover .tacts,.tnode:focus-within .tacts{opacity:1}


/* --- number fields -------------------------------------------------------
   The platform's own spinners are the last 1997 control on the page: tiny,
   hover-only in some browsers, and drawn by the OS rather than by us. */
input[type=number]::-webkit-outer-spin-button,
input[type=number]::-webkit-inner-spin-button{-webkit-appearance:none;margin:0}
input[type=number]{-moz-appearance:textfield;appearance:textfield}

.stepper{display:inline-flex;align-items:stretch;border:1px solid var(--line);
  border-radius:7px;overflow:hidden;background:var(--bg);vertical-align:middle}
.stepper:focus-within{outline:2px solid var(--orange);outline-offset:-1px;border-color:var(--orange)}
.stepper input[type=number]{border:0!important;border-radius:0!important;outline:0!important;
  background:transparent;text-align:center;padding:5px 2px;width:52px;min-width:0;
  font:inherit;font-size:13px;color:var(--ink);-moz-appearance:textfield}
.st-btn{border:0;border-radius:0;background:transparent;color:var(--muted);
  font-size:14px;font-weight:600;line-height:1;padding:0 9px;cursor:pointer;
  display:flex;align-items:center;justify-content:center;min-width:26px}
.st-btn:hover:not(:disabled){background:rgba(255,126,5,.15);color:var(--orange);filter:none}
.st-btn:active:not(:disabled){background:rgba(255,126,5,.28)}
.st-btn:disabled{opacity:.3;cursor:default}
.st-dn{border-right:1px solid var(--line)}
.st-up{border-left:1px solid var(--line)}

/* Tighter inside dense rows */
.tqty .stepper input[type=number]{width:44px}
.opstable .stepper{width:100%}
.opstable .stepper input[type=number]{width:100%;text-align:right;padding-right:6px}
#parts .stepper input[type=number]{width:56px}
.fieldgrid .stepper{width:100%}
.fieldgrid .stepper input[type=number]{width:100%;text-align:right;padding-right:8px}

/* A stepper is wider than a bare field, so the longest label was pushing the
   quantity onto its own line. The label gives way instead of the control. */
.tnode{flex-wrap:nowrap}
/* The label takes the free space so it stops ellipsising at four characters;
   it only gives way once the row genuinely runs out of room. */
.tnode .tlabel{flex:1 1 auto;min-width:0;overflow:hidden;text-overflow:ellipsis;
  white-space:nowrap}
/* The stepper reads as a quantity on its own; a times sign in front of it was
   a second thing to look at saying what the first already said. It sits close
   to the name it belongs to rather than adrift between the two. */
.tnode .tqty{flex:0 0 auto;white-space:nowrap;display:flex;align-items:center;
  margin-left:-4px}

/* Save status sits where the "unsaved changes" note used to. */
.rt-total .savest{font-size:12px;color:var(--muted);transition:opacity .2s}
.rt-total .savest.on{color:var(--green)}


/* --- the 3D viewer, at a size worth opening ------------------------------- */
.viewmodal{padding:3vh 3vw;align-items:stretch}
.viewinner{max-width:none;width:100%;display:flex;flex-direction:column}
.viewinner .viewwrap{flex:1;height:auto;min-height:0;position:relative}
.vwdims{position:absolute;left:14px;bottom:14px;display:flex;flex-direction:column;gap:2px;
  background:rgba(0,0,0,.55);border:1px solid rgba(255,255,255,.14);border-radius:9px;
  padding:9px 13px;backdrop-filter:blur(3px);pointer-events:none}
.vwd-main{font-size:16px;font-weight:620;color:#fff;font-variant-numeric:tabular-nums}
.vwd-sub{font-size:11.5px;color:rgba(255,255,255,.66)}

/* --- summary strip -------------------------------------------------------- */

.stat.src-make{border-color:rgba(46,125,50,.45);background:rgba(46,125,50,.07)}
.stat.src-make .v{color:#5fb567}
.stat.src-buy{border-color:rgba(150,105,225,.45);background:rgba(150,105,225,.07)}
.stat.src-buy .v{color:#b18cf0}
.stat.src-sub{border-color:rgba(240,165,0,.45);background:rgba(240,165,0,.07)}
.stat.src-sub .v{color:var(--amber)}
.stat.complete{grid-column:span 2}
.stat.complete .of{font-size:15px;font-weight:500;color:var(--muted)}
.stat.complete.done{border-color:rgba(46,125,50,.5);background:rgba(46,125,50,.08)}
.stat.complete.done .v{color:#5fb567}
.stat.complete.short{border-color:var(--amber);background:rgba(240,165,0,.08)}
.stat.complete.short .v{color:var(--amber)}
.cbar{height:4px;border-radius:3px;background:rgba(128,128,128,.25);margin-top:8px;overflow:hidden}
.cbar span{display:block;height:100%;background:currentColor;transition:width .3s}
.stat.complete.done .cbar span{background:#5fb567}
.stat.complete.short .cbar span{background:var(--amber)}

/* --- one summary strip ---------------------------------------------------- */
.summary{display:grid;grid-template-columns:repeat(auto-fill,minmax(150px,1fr));gap:12px;
  margin:22px 0 16px}
.stat.jumpable{cursor:pointer}
.stat.jumpable:hover{filter:brightness(1.14)}
.stat.jumpable:active{transform:translateY(1px)}

/* A flash, because on a long quote the selection highlight alone is missable. */
@keyframes cadqflash{0%{background:rgba(255,126,5,.42)}100%{background:transparent}}
.tnode.flash,#parts tr.flash{animation:cadqflash 1.3s ease-out}
#parts tr.flash td{animation:cadqflash 1.3s ease-out}

/* The row actions are hidden until hover, but opacity still reserves their
   width -- five buttons worth of it -- which was squeezing every part name into
   an ellipsis. Taken out of the flow so the label gets the room, and given the
   row a right inset so they never sit on top of it. */
.tnode{position:relative;padding-right:190px}
.tnode .tacts{position:absolute;right:8px;top:50%;transform:translateY(-50%);margin-left:0}
.tnode .tlabel{flex:0 1 auto}

/* Reorder shows a line where the row will land; reparent highlights the row it
   will go into. Two different outcomes need two different signals. */
.tnode.drop-before{box-shadow:inset 0 3px 0 var(--orange)}
.tnode.drop-after{box-shadow:inset 0 -3px 0 var(--orange)}

/* --- the parts table as a decision surface -------------------------------- */
.rdy{font-size:11.5px;font-weight:600;padding:2px 7px;border-radius:5px;display:inline-block}
.rdy.ok{background:rgba(46,125,50,.18);color:#5fb567}
.rdy.short{background:rgba(240,165,0,.16);color:var(--amber);max-width:150px;overflow:hidden;
  text-overflow:ellipsis;white-space:nowrap}
.moneytog{display:flex;align-items:center;gap:6px;font-size:12px;color:var(--muted);
  cursor:pointer;padding-left:8px;border-left:1px solid var(--line);margin-left:2px}
.moneytog input{width:14px;height:14px;accent-color:var(--orange);cursor:pointer}
.moneytog:hover{color:var(--ink)}
#parts th.money,#parts td.money{color:var(--muted)}

/* The row is a target now, so it should look like one. */
#parts tbody tr{cursor:pointer}
#parts tbody tr:hover{background:rgba(128,128,128,.06)}
#parts tbody tr.selected:hover{background:rgba(255,126,5,.10)}


/* --- the big preview ------------------------------------------------------ */
.hoverpreview.big{position:fixed;left:50%;top:50%;transform:translate(-50%,-50%);
  width:min(62vw,720px);max-height:82vh;padding:18px 18px 12px;border-radius:14px;
  box-shadow:0 24px 70px rgba(0,0,0,.5);z-index:70}
.hoverpreview.big img{width:100%;height:auto;max-height:70vh;object-fit:contain}
.hoverpreview.big .hp-label{font-size:14px;margin-top:10px;text-align:center}
.hp-hint{font-size:11.5px;color:var(--muted);margin-left:8px;text-transform:uppercase;
  letter-spacing:.06em;font-weight:700}
#parts .thumb img{transition:transform .12s}
#parts .thumb img:hover{transform:scale(1.08)}

.hp-dims{font-size:12.5px;color:var(--muted);text-align:center;margin-top:3px;
  font-variant-numeric:tabular-nums}
.hoverpreview.big .hp-dims{font-size:14px;color:var(--ink);font-weight:560}
#parts tr.manualrow td.thumb{background:none}
#parts tr.manualrow .plabel{text-decoration-style:dotted}

/* --- components consumed at operations ------------------------------------ */
.opstable td.consumes{max-width:210px}
.dropnote{font-size:11px;color:var(--muted);opacity:.65;font-style:italic}
.opstable tr.dropinto td{background:rgba(255,126,5,.14);box-shadow:inset 0 0 0 2px var(--orange)}
.cchip{display:inline-flex;align-items:center;gap:5px;background:rgba(150,105,225,.20);
  color:#b18cf0;border-radius:5px;padding:2px 7px;font-size:11.5px;font-weight:600;
  margin:2px 4px 2px 0;cursor:pointer;max-width:100%}
.cchip:hover{background:rgba(198,40,40,.20);color:var(--red)}
.cchip .cq{font-weight:400;opacity:.8}

.blockline{display:flex;gap:5px;align-items:center;flex-wrap:wrap;margin-top:4px}
.bchip{background:rgba(240,165,0,.18);color:var(--amber);border-radius:5px;padding:1px 7px;
  font-size:11px;font-weight:600;cursor:pointer}
.bchip:hover{background:rgba(198,40,40,.20);color:var(--red)}
.blocksel{font-size:11px;padding:2px 20px 2px 6px;background-size:9px;
  background-position:right 6px center}

/* --- the bill of materials, inside the routing pane ----------------------- */
.bompane{margin-top:6px}
.complist{display:flex;flex-direction:column;border:1px solid var(--line);border-radius:10px;
  overflow:hidden;margin-top:10px}
.comprow{display:grid;grid-template-columns:22px minmax(0,1fr) 84px 96px 190px;gap:10px;
  align-items:center;padding:7px 10px;border-bottom:1px solid var(--line);cursor:grab;
  font-size:13px}
.comprow:last-child{border-bottom:0}
.comprow:hover{background:rgba(128,128,128,.07)}
.comprow.dragging{opacity:.4}
.cgrip{color:var(--muted);display:flex;align-items:center}
.cname{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;font-weight:550}
.cqty{text-align:right;font-variant-numeric:tabular-nums;color:var(--muted);font-size:12.5px}
.ctype{font-size:11px;color:var(--muted);text-transform:uppercase;letter-spacing:.05em}
.catsel{font-size:12px;padding:4px 22px 4px 8px}
.ppnoimg{width:40px;height:40px;display:grid;place-items:center;color:var(--muted);font-size:16px}
@media (max-width:1200px){
  .comprow{grid-template-columns:22px minmax(0,1fr) 70px 170px;row-gap:4px}
  .ctype{display:none}
}

/* Blocked by sits where the basis used to, so an operation is one line. */
.opstable td.blockcell{max-width:190px}
.blockcell .blockline{margin-top:0}

.conline{display:flex;gap:4px;align-items:center;flex-wrap:wrap}
.consel{font-size:11px;padding:2px 20px 2px 6px;background-size:9px;
  background-position:right 6px center;max-width:100%}

/* --- routing table column widths -----------------------------------------
   A stepper is wider than the number in it, so run and setup were taking room
   the two link columns needed, and the consumes chips spilled into blocked-by.
   Widths are set on the columns rather than left to the content, and the cells
   that hold chips clip and wrap instead of overflowing their neighbour. */
.opstable th:nth-child(4),.opstable td:nth-child(4),
.opstable th:nth-child(5),.opstable td:nth-child(5){width:88px}
.opstable td:nth-child(4) .st-btn,.opstable td:nth-child(5) .st-btn{
  min-width:20px;padding:0 5px;font-size:13px}
.opstable td:nth-child(4) .stepper input,.opstable td:nth-child(5) .stepper input{
  width:100%;min-width:0;padding-left:2px;padding-right:4px}

.opstable th:nth-child(6),.opstable td:nth-child(6){width:230px;max-width:230px}
.opstable th:nth-child(7),.opstable td:nth-child(7){width:150px;max-width:150px}
.opstable td.consumes,.opstable td.blockcell{overflow:hidden}
.conline,.blockline{max-width:100%;min-width:0}
.cchip,.bchip{max-width:100%;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.consel,.blocksel{max-width:100%;min-width:0}


/* --- type-to-narrow field ------------------------------------------------
 *
 * Positioned on the body rather than inside the row: the routing table scrolls
 * and clips, and a list anchored inside it gets cut off at the last row --
 * which is exactly where you are when adding an operation.
 *
 * Colours come from the real tokens. The first cut invented --card and --hover,
 * which do not exist here, so the background fell back to a hardcoded white
 * while the text kept inheriting --ink. Readable in light, white on white in
 * dark. A fallback colour that only works in one scheme is worse than none.
 */
.combopop {
  position: absolute;
  z-index: 60;
  max-height: 268px;
  overflow-y: auto;
  background: var(--panel);
  color: var(--ink);
  border: 1px solid var(--line);
  border-radius: 8px;
  box-shadow: 0 10px 28px rgba(0, 0, 0, .22);
  padding: 4px;
  font-size: 13px;
}
.comboitem {
  display: flex;
  align-items: baseline;
  gap: 10px;
  padding: 6px 9px;
  border-radius: 6px;
  cursor: pointer;
  white-space: nowrap;
  color: var(--ink);
}
.comboitem em {
  margin-left: auto;
  font-style: normal;
  color: var(--muted);
  font-size: 11px;
}
/* Tinted from the brand orange rather than a fixed grey, so the highlight is
   visible against both the light and the dark panel. */
.comboitem.on, .comboitem:hover {
  /* A neutral translucent first, so the highlight still shows where color-mix
     is not supported -- an unsupported value is dropped silently and the row
     would simply stop highlighting. */
  background: rgba(128, 128, 128, .22);
  background: color-mix(in srgb, var(--orange) 16%, transparent);
}
.comboempty, .combomore {
  padding: 6px 9px;
  color: var(--muted);
  font-size: 12px;
}
.combomore { border-top: 1px solid var(--line); margin-top: 2px; }

/* The step number a blocked-by choice refers to. Without it on the row, "3. MIG
   weld" in the dependency list means counting down the table to find which weld
   that is -- which is the counting the number was added to save. */
.opstable .drag { white-space: nowrap; }
.stepno {
  display: inline-block;
  min-width: 1.3em;
  margin-right: 4px;
  color: var(--muted);
  font-size: 12px;
  font-variant-numeric: tabular-nums;
  text-align: right;
}

/* --- messages ------------------------------------------------------------
   Over the page, top centre, self-dismissing. In the flow they pushed the work
   down the screen as they arrived and stayed after they stopped being true. */
.toasts{
  position:fixed; top:14px; left:50%; transform:translateX(-50%);
  z-index:200; display:flex; flex-direction:column; gap:8px;
  align-items:center; pointer-events:none; max-width:min(680px,92vw);
}
.toast{
  pointer-events:auto; cursor:pointer;
  border-radius:10px; padding:10px 16px; font-size:13.5px;
  border:1px solid var(--line); background:var(--panel); color:var(--ink);
  box-shadow:0 8px 26px rgba(0,0,0,.18);
  opacity:0; transform:translateY(-8px);
  transition:opacity .18s ease, transform .18s ease;
}
.toast-in{opacity:1; transform:translateY(0)}
.toast-out{opacity:0; transform:translateY(-8px)}
.toast.warn{border-color:var(--amber); background:color-mix(in srgb,var(--amber) 12%,var(--panel))}
.toast.err{border-color:var(--red);   background:color-mix(in srgb,var(--red) 12%,var(--panel))}
.toast.ok{border-color:var(--green);  background:color-mix(in srgb,var(--green) 12%,var(--panel))}

/* The undo button names what it will undo, so pressing it is not a guess.
   Truncated rather than allowed to push the account cluster off the header. */
#undoHdr .undo-what{
  opacity:.6; margin-left:6px; max-width:16ch;
  overflow:hidden; text-overflow:ellipsis; white-space:nowrap;
  display:inline-block; vertical-align:bottom;
}
#undoHdr:disabled{opacity:.4}
#undoHdr:disabled .undo-what{display:none}
