:root{
  --ink:#1b1917; --slate:#4a534d; --sage:#7d8b74; --clay:#3E6FA8;
  --paper:#e9e5dd; --card:#f5f3ee; --line:#c9c3b6; --soft:#ddd8cc;
  --ok:#4a7a52; --warn:#b4643c;
  /* THE MAROON, kept on Nick's instruction (2026-09-16). It is the colour of
     the Areas room chips on production engine-0.59.9 / ui-0.51.2 — read out of
     `.areahead .ttl` there, not chosen here, and byte-identical across
     ec3ef03, 18a0a6f and a4d9926, so it is the shipped value and not a
     moment in a refactor.

     ONE token, and it means SELECTION: the room chip, the active right-panel
     tab, a selected list row, and the selected object's marks in the Selected
     tab. It is deliberately NOT --clay, which is the blue used for links,
     focus rings and the spec browser — those are not selection and they stay
     blue. Anything that wants "the maroon" uses this name, so the next person
     who has to find it has one place to look instead of nine hardcoded
     copies of it. */
  --ksp-selection:#6b2434;
  --display:'Archivo',system-ui,sans-serif;
  --body:'Inter',system-ui,sans-serif;
  --mono:'Roboto Mono',monospace;
  /* The right panel's column width, and the width of the toolbar band pinned
     over it (ui-1.28.0). ONE number: two would let the Space/finish/price
     strip drift off the tabs it is centred on, and the drift would only show
     at some window widths. */
  --panelw:360px;
  /* The 2D LABEL face (ui-1.26.0): the plan's cabinet codes, the letter
     tags and the key under the plan all read in this, not in the mono.
     `tabular-nums` is set where it can be — these are real HTML, unlike
     the canvas labels, where a 2D context has no way to ask for it. */
  --label-sans:system-ui,-apple-system,'Segoe UI',Roboto,'Helvetica Neue',Arial,sans-serif;
}
*{box-sizing:border-box;margin:0;padding:0}
body{font-family:var(--body);background:var(--paper);color:var(--ink);
  height:100vh;overflow:hidden;display:flex;flex-direction:column;-webkit-font-smoothing:antialiased}

/* Header — charcoal + steel blue. The version stamp sits UNDER the wordmark
   (stacked, not beside it) so it costs no horizontal space, and the accent rule
   below the header is a thin 4px line. Chrome stays short: every pixel here is
   taken from the drawing board. */
header{display:grid;grid-template-columns:minmax(0,1fr) auto minmax(0,1fr);align-items:center;gap:18px;padding:6px 18px;
  background:#1d1f22;color:var(--paper);border-bottom:4px solid #185FA5;flex-shrink:0}
/* ui-0.63.0: the brand is the CENTRE zone again, alone, with the stamp
   centred under the wordmark. */
.brandwrap{display:flex;flex-direction:column;align-items:center;text-align:center;line-height:1.1;flex-shrink:0}
.brand{font-family:var(--display);font-weight:700;font-size:16px;letter-spacing:-.02em;
  flex-shrink:0;line-height:1.05}
.brand span{color:#85B7EB}
#verstamp{color:#82868c !important;font-family:var(--mono);font-size:9px;
  font-weight:500;letter-spacing:0;margin-left:0;line-height:1.1}
.tag{font-size:11.5px;color:#82868c;display:none}
.navmode{display:flex;border:1px solid #33373c;border-radius:3px;overflow:hidden;margin-left:2px}
.navmode button{border:0;border-radius:0;background:transparent;color:#d2d5d9;
  font-family:var(--body);font-size:12.5px;font-weight:600;padding:7px 16px;cursor:pointer}
.navmode button+button{border-left:1px solid #33373c}
.navmode button:hover{color:#fff;background:#282b30}
.navmode button.on{background:#185FA5;color:#fff}
/* Elevation Clean / Dimensioned: the handlers move `on` between them and nothing
   styled it, so both looked the same and Clean read as "no measurements"
   (finding 4, Nick 2026-10-02). Same blue as the view switch. */
/* the Details card's other-heights row (brief ad-s10) */
.selhts{display:flex;gap:4px;flex-wrap:wrap}
.selhtc{padding:3px 8px;font:600 11px Roboto Mono,monospace;border:1px solid var(--line,#c9c3b8);background:#fff;border-radius:3px;cursor:pointer}
.selhtc.on{background:#185FA5;color:#fff;border-color:#185FA5;cursor:default}
#elevtools button.on{background:#185FA5;color:#fff;border-color:#185FA5}
.roommenu{position:fixed;z-index:1200;min-width:200px;background:#fff;
  border:1px solid var(--line);border-radius:8px;overflow:hidden;
  box-shadow:0 8px 24px rgba(0,0,0,.12),0 2px 6px rgba(0,0,0,.08)}
.roommenu .rmitem{display:flex;align-items:center;gap:9px;padding:9px 13px;
  font-size:13px;cursor:pointer;color:var(--slate)}
.roommenu .rmitem:hover{background:#eef2f6}
.roommenu .rmitem.on{background:#f6f8fa;color:var(--ink);font-weight:600}
.roommenu .rmtick{width:14px;display:inline-block;text-align:center;
  color:#185FA5;font-size:12px}
.roommenu .rmsep{border-top:1px solid var(--line)}
.roommenu .rmhd{padding:7px 13px 4px;font-size:10px;letter-spacing:.06em;
  text-transform:uppercase;color:var(--slate);opacity:.75}
.matpop{position:fixed;z-index:1200;overflow-y:auto;overscroll-behavior:contain;min-width:250px;background:#fff;
  border:1px solid var(--line);border-radius:8px;padding:12px 14px;
  box-shadow:0 8px 24px rgba(0,0,0,.12),0 2px 6px rgba(0,0,0,.08)}
.matpop .mprow{display:flex;align-items:center;gap:10px;margin-bottom:10px}
.matpop .mprow:last-child{margin-bottom:0}
.matpop .mplbl{font-size:12px;color:var(--slate);width:56px;flex-shrink:0}
.matpop .mpsw{width:24px;height:24px;border-radius:5px;border:1px solid var(--line);
  background:#d8d5cf;flex-shrink:0}
.matpop .mplink{border:0;background:transparent;color:#185FA5;font-size:12px;
  cursor:pointer;padding:2px 0}
.matpop .mplink:hover{text-decoration:underline}
.matpop .mpsep{border-top:1px solid var(--line);margin:10px 0}
.magsw{width:34px;height:19px;border-radius:10px;border:0;background:#c9ccd1;
  position:relative;cursor:pointer;padding:0;flex-shrink:0}
.magsw::after{content:"";position:absolute;left:2px;top:2px;width:15px;height:15px;
  border-radius:50%;background:#fff;transition:left .12s}
.magsw.on{background:#185FA5}
.magsw.on::after{left:17px}
.hr{margin-left:auto;display:flex;gap:8px;align-items:center}

/* The right panel is 360px, and 440px with the Order view open (Nick,
   2026-09-16, with docs/design/crisp-blue/1-workspace.png and 2-order-panel.png
   as the layout). It was 206px, which is where every clipped control in this
   app came from: the order table's EXT column, "Line Ma…", and Brand /
   Collection / Door style showing two or three letters each. The order needs
   the extra 80px for a SKU / Item / Qty / Price row that fits on ONE line.
   `main.wide` (minimized) still wins — it is written after both. */
main{flex:1;display:grid;grid-template-columns:72px minmax(0,1fr) var(--panelw);min-height:0}
/* 30px, matching the handle's own width (ui-1.32.0). At 28 the button
   overflowed the column by 2px and ran off the right edge of a
   1920-wide screen — measured, not guessed. */
main.wide{grid-template-columns:72px minmax(0,1fr) 30px}
/* Minimized (ui-0.65.0): the panel is a 28px strip holding only its toggle. */
main.wide #sideL>:not(#ptabs){display:none}
main.wide #ptabs{flex:1;padding:0;border:0;background:transparent}
main.wide #ptabs .ptab{display:none}
.panel{display:flex;flex-direction:column;min-height:0;background:var(--card);
  min-width:0;overflow:hidden}
.panel+.panel,.panel+.mid,.mid+.panel{border-left:1px solid var(--line)}
.mid{background:var(--paper);display:flex;flex-direction:column;min-height:0;min-width:0}
.sidehd{display:flex;align-items:center;gap:8px;padding:11px 14px 9px;
  border-bottom:1px solid var(--soft);flex-shrink:0}
.sidehd .ttl{font-family:var(--display);font-size:10.5px;font-weight:600;
  letter-spacing:.09em;text-transform:uppercase;color:var(--slate)}
.sidehd .xclose{margin-left:auto;background:transparent;border:1px solid var(--line);
  color:var(--slate);width:28px;height:28px;padding:0;border-radius:3px;
  font-size:16px;line-height:1;cursor:pointer}
.sidehd .xclose:hover{background:var(--ink);color:var(--paper);border-color:var(--ink)}
.ph{font-family:var(--display);font-size:10.5px;font-weight:600;letter-spacing:.09em;
  text-transform:uppercase;color:var(--slate);padding:11px 15px 9px;
  border-bottom:1px solid var(--soft);display:flex;gap:7px;align-items:center;flex-shrink:0}
.ph .n{font-family:var(--mono);color:var(--clay)}
/* min-height:0 is what actually lets this scroll. A flex item's default
   min-height is auto, so a tall child makes the item grow instead of
   overflowing, and `overflow-y:auto` then has nothing to do — the whole panel
   scrolls and the pinned chrome above goes with it. */
.pb{flex:1;min-height:0;overflow-x:hidden;overflow-y:auto;padding:12px 14px 28px;
  scrollbar-gutter:stable}

label.f{display:block;font-size:9.5px;font-weight:600;letter-spacing:.07em;
  text-transform:uppercase;color:var(--slate);margin-bottom:4px}
.fld{margin-bottom:11px}
select,input{width:100%;max-width:100%;padding:7px 8px;border:1px solid var(--line);
  border-radius:3px;background:#fff;font-family:var(--body);font-size:12.5px;
  color:var(--ink);min-width:0;box-sizing:border-box}
select:focus,input:focus{outline:2px solid var(--sage);outline-offset:-1px}
.row{display:flex;gap:6px}.row>*{flex:1;min-width:0}
.row .wl{font-family:var(--mono);font-size:12px;padding:6px 4px}

button{font-family:var(--body);font-size:12.5px;font-weight:600;padding:7px 13px;
  border:1px solid var(--ink);border-radius:3px;background:var(--ink);color:var(--paper);
  cursor:pointer;transition:background .12s}
button:hover{background:var(--clay);border-color:var(--clay)}
button:focus-visible{outline:2px solid var(--sage);outline-offset:2px}
button.g{background:transparent;color:var(--ink);border-color:var(--line)}
button.g:hover{background:var(--ink);color:var(--paper)}
button.s{padding:5px 10px;font-size:11.5px}

.tabs{display:flex;border:1px solid var(--line);border-radius:3px;overflow:hidden;margin-bottom:12px}
.tabs button{flex:1;border:0;border-radius:0;background:#fff;color:var(--slate);
  font-size:11.5px;padding:7px 3px}
.tabs button+button{border-left:1px solid var(--line)}
.tabs button.on{background:var(--slate);color:var(--paper)}

.th{display:flex;align-items:center;gap:6px;font-family:var(--display);font-size:10px;
  font-weight:600;letter-spacing:.06em;text-transform:uppercase;color:var(--slate);
  margin:13px 0 6px;padding-bottom:4px;border-bottom:1px solid var(--soft)}
.th .len{margin-left:auto;font-family:var(--mono);font-size:10.5px;font-weight:400;
  letter-spacing:0;text-transform:none}
.th .len.over{color:var(--warn);font-weight:500}

.it{display:flex;align-items:center;gap:7px;padding:6px 8px;background:#fff;
  border:1px solid var(--soft);border-radius:3px;margin-bottom:4px;font-size:12px}
.it .c{font-family:var(--mono);font-weight:500}
.tagno{display:inline-flex;align-items:center;justify-content:center;min-width:16px;height:16px;padding:0 3px;border-radius:8px;background:#2850a0;color:#fff;font-size:10px;font-weight:800;vertical-align:middle}
.it button{background:transparent;border:0;color:var(--line);font-size:15px;
  line-height:1;padding:0 2px}
.it button:hover{color:var(--warn);background:transparent}

.empty{font-size:11.5px;color:var(--slate);opacity:.6;padding:6px 0 8px;font-style:italic}
.add{display:flex;gap:5px;margin-top:5px}
.add select{flex:1;font-size:11.5px;padding:5px 6px;min-width:0}
.add select.cabpick{font-family:var(--mono);font-size:11px;letter-spacing:0}
.add button{flex-shrink:0;white-space:nowrap}

/* The drawing area is WHITE — a neutral canvas reads cleaner and makes the
   cabinet colours truer. The beige `--paper` stays for the surrounding chrome. */
#stage{flex:1;position:relative;min-height:0;background:#ffffff}
#cablegend{position:absolute;top:52px;right:10px;z-index:5;background:#fbfaf7;
  border:1px solid var(--line);border-radius:10px;padding:10px 10px 8px;
  box-shadow:0 6px 22px rgba(40,36,32,.14);max-height:60%;overflow:auto;min-width:150px}
#cablegend h5{margin:0 0 8px;font-size:9px;text-transform:uppercase;letter-spacing:.06em;
  color:var(--slate);font-weight:700;cursor:grab;user-select:none;
  display:flex;align-items:center;gap:6px}
#cablegend h5::before{content:"⠿";font-size:12px;color:var(--line);letter-spacing:-1px}
#cablegend.dragging h5{cursor:grabbing}
#cablegend .lgsub{margin:10px 0 5px;font-size:9px;text-transform:uppercase;
  letter-spacing:.06em;color:var(--slate);font-weight:700;display:flex;
  align-items:center;gap:6px;border-top:1px solid var(--line);padding-top:8px}
#cablegend .lr.sug{opacity:.85}
#cablegend .lr.sug .lk{font-style:italic}
#cablegend .sugadd{margin-left:6px;width:20px;height:20px;border-radius:5px;
  border:1px solid #185FA5;background:#eaf2fb;color:#185FA5;font-size:13px;
  line-height:1;cursor:pointer;flex-shrink:0}
#cablegend .sugadd:hover{background:#185FA5;color:#fff}
#cablegend .lr{display:flex;align-items:center;gap:8px;padding:5px 6px;border-radius:7px;
  cursor:pointer;font-family:var(--label-sans);font-size:11px;
  font-variant-numeric:tabular-nums;border:1px solid transparent}
#cablegend .lr:hover{background:#fff;border-color:var(--line)}
/* THE PICK IS MARKED IN THE LIST (ui-1.90.x, Nick 2026-10-04 "3D selection does
   something"): the same blue as the 3D outline and the plan's highlight. */
#cablegend .lr.sel{background:#eaf2fb;border-color:#185FA5}
#cablegend .ld{width:18px;height:18px;border-radius:50%;display:flex;align-items:center;
  justify-content:center;color:#fff;font-family:var(--label-sans);font-weight:600;
  font-variant-numeric:tabular-nums;font-size:10px;flex:0 0 auto}
/* THE LETTER TAG IS SQUARE (ui-1.20.0). The round marker is the WALL's; a
   cabinet's is a rounded square with a letter in it, so the two series cannot
   be confused — on screen, and in a printed plan with no colour. */
/* LABELS: Codes / Tags (ui-1.20.0) — the segmented control in the 2D view
   tools. It borrows .seg2, the same piece the view switcher uses, so it reads
   as one of the view controls rather than a fourth kind of button. */
/* THE KEY UNDER THE PLAN (ui-1.20.0). One row, scrolls sideways rather than
   growing tall — the drawing is what the space is for — and it prints. */
#taglegend{display:flex;align-items:center;gap:10px;flex-wrap:wrap;
  padding:6px 12px;border-top:1px solid var(--line);background:var(--card);
  font-size:11.5px;max-height:84px;overflow-y:auto}
#taglegend[hidden]{display:none!important}
#taglegend .tlhd{color:var(--slate);text-transform:uppercase;letter-spacing:.06em;
  font-weight:800;font-size:10.5px}
#taglegend .tlitem{display:inline-flex;align-items:center;gap:4px}
#taglegend .tltag{display:inline-flex;align-items:center;justify-content:center;
  min-width:17px;height:17px;padding:0 4px;border-radius:4px;color:#fff;
  border:1.5px solid transparent;
  font:600 10.5px var(--label-sans);font-variant-numeric:tabular-nums}
/* An UPPER's chip is inverted, exactly as its tag on the plan is: white,
   with the ink as letter and border (ui-1.20.1). The border colour is set
   inline from the same ink the canvas uses, so the two cannot drift. */
#taglegend .tltag.up,#cablegend .ld.sq.up,#fillerbox .ld.sq.up{
  background:#fff;border-style:solid;border-width:1.5px;
  box-shadow:0 1px 2px rgba(20,26,34,.18)}
#taglegend .tlsku{font:600 11.5px var(--label-sans);font-variant-numeric:tabular-nums;
  color:var(--ink)}
.lblseg{font-size:12px}
.lblseg button{padding:3px 10px}
#labeltools{display:flex;align-items:center;gap:6px}
/* NO "LABELS" CAPTION (ui-1.28.0, Nick). Codes and Tags say what they are,
   and the word was a seventh thing to read in a bar that is already dense. */
#cablegend .ld.sq,#fillerbox .ld.sq{border-radius:4px;min-width:18px;width:auto;
  padding:0 4px;font-family:var(--label-sans);
  border:1.5px solid transparent;box-sizing:border-box}
#cablegend .lk{font-weight:700}
#cablegend .lv{color:var(--slate);margin-left:auto;padding-left:8px}
#cablegend[hidden]{display:none!important}
#cablegend.min{padding:0;min-width:0;max-height:none;overflow:visible;background:transparent;border:0;box-shadow:none}
#cablegend.min h5,#cablegend.min .lr{display:none}
#cabpill{position:fixed;z-index:60;display:inline-flex;align-items:center;gap:7px;
  background:#fbfaf7;border:1px solid var(--line);border-radius:20px;
  padding:7px 13px 7px 11px;box-shadow:0 4px 16px rgba(40,36,32,.16);
  cursor:pointer;font-family:'Roboto Mono',monospace;font-size:12px;
  color:var(--ink);user-select:none}
#cabpill:hover{border-color:var(--clay);color:var(--clay)}
#cabpill .cpdot{width:8px;height:8px;border-radius:50%;background:var(--clay)}
#cabpill .cphandle{color:var(--line);font-size:13px;margin-right:1px;cursor:grab}
#cabpill.dragging .cphandle{cursor:grabbing}
#cabpill[hidden]{display:none!important}
#cabpill:not(.open){padding:4px 8px 4px 9px;font-size:11px;gap:5px}
#cabpill .cpcount{cursor:pointer}
#cabpill .cpcount:hover{text-decoration:underline}
/* While the draw-walls bar is up it sits over the pill's home corner at
   1280x720, 1366x768 and phone width (job 1010-0054-s9). Lift the pill clear
   of it: CSS only, and only while the bar exists; a dragged pill (top/left)
   is the user's own placement and is left alone. */
body:has(#drawbar) #cabpill{margin-bottom:74px}
#cabpill .cpbtn{width:20px;height:20px;padding:0;margin-left:2px;border:1px solid var(--line);
  border-radius:6px;background:#fff;color:var(--slate);font-size:12px;line-height:1;
  display:inline-flex;align-items:center;justify-content:center;cursor:pointer}
#cabpill .cpbtn:hover{border-color:var(--clay);color:var(--clay);background:#fff}
#cablegend h5 .lgbtn{margin-left:auto;width:20px;height:20px;padding:0;border:1px solid var(--line);
  border-radius:6px;background:#fff;color:var(--slate);font-size:13px;line-height:1;
  display:inline-flex;align-items:center;justify-content:center;cursor:pointer}
#cablegend h5 .lgbtn:hover{border-color:var(--clay);color:var(--clay)}
/* the Left / Right gap box under the plan (job 1007-1929) */
#sidegapbox{position:absolute;left:50%;bottom:12px;transform:translateX(-50%);z-index:6;
  display:flex;flex-direction:column;background:#fbfaf7;border:1px solid var(--line);
  border-radius:10px;padding:6px 4px;box-shadow:0 6px 22px rgba(40,36,32,.14);
  font-size:12px;color:var(--slate);max-width:calc(100% - 32px)}
#sidegapbox[hidden]{display:none}
#sidegapbox .sgto{color:var(--slate);opacity:.75;white-space:nowrap;margin-left:4px}
/* auto-filler box: same chrome as the cabinet legend, stacked just below it */
#fillerbox{position:absolute;right:10px;z-index:5;background:#fbfaf7;
  border:1px solid var(--line);border-radius:10px;padding:10px 10px 8px;
  box-shadow:0 6px 22px rgba(40,36,32,.14);max-height:40%;overflow:auto;min-width:150px}
#fillerbox h5{margin:0;font-size:9px;text-transform:uppercase;letter-spacing:.06em;
  color:var(--slate);font-weight:700;cursor:grab;user-select:none;
  display:flex;align-items:center;gap:6px}
#fillerbox h5::before{content:"⠿";font-size:12px;color:var(--line);letter-spacing:-1px}
#fillerbox.dragging h5{cursor:grabbing}
#fillerbox.open h5{margin-bottom:8px}
#fillerbox .lr{display:flex;align-items:center;gap:8px;padding:5px 6px;border-radius:7px;
  font-family:'Roboto Mono',monospace;font-size:11px;border:1px solid transparent}
#fillerbox .ld{width:18px;height:18px;border-radius:50%;display:flex;align-items:center;
  justify-content:center;color:#fff;font-weight:800;font-size:10px;flex:0 0 auto}
#fillerbox .lk{font-weight:700}
#fillerbox .lv{color:var(--slate);margin-left:auto;padding-left:8px}
#fillerbox[hidden]{display:none!important}
#fillerbox h5 .lgbtn{margin-left:auto;width:20px;height:20px;padding:0;border:1px solid var(--line);
  border-radius:6px;background:#fff;color:var(--slate);font-size:13px;line-height:1;
  display:inline-flex;align-items:center;justify-content:center;cursor:pointer}
#fillerbox h5 .lgbtn:hover{border-color:var(--clay);color:var(--clay)}
#specOverlay{position:fixed;inset:0;z-index:50;background:var(--paper,#f4f1ea);
  display:flex;flex-direction:column}
.specbar{display:flex;align-items:center;gap:12px;padding:12px 18px;
  border-bottom:1px solid var(--line,#d8d4cb);background:var(--card,#fff);flex-shrink:0}
.spectitle{font-family:var(--display,Georgia),serif;font-size:13px;font-weight:600;
  letter-spacing:.08em;text-transform:uppercase;color:var(--slate,#5a544a)}
#specSearch{flex:1;max-width:440px;font:inherit;font-size:13px;padding:7px 12px;
  border:1px solid var(--line,#d8d4cb);border-radius:6px;background:transparent}
#specSearch:focus{outline:none;border-color:var(--clay,#7B1420)}
.speccount{font-size:11px;color:var(--slate,#8a857c);margin-left:auto}
.specchips{display:flex;gap:6px;flex-wrap:wrap;padding:8px 18px;
  border-bottom:1px solid var(--soft,#eee);align-items:center}
.specchips .chlabel{font-size:10px;text-transform:uppercase;letter-spacing:.05em;
  color:var(--slate,#8a857c);margin-right:2px}
.specmore{grid-column:1/-1;justify-self:center;margin:8px 0;padding:8px 18px;border:1px solid var(--line,#d8d4cb);background:transparent;font:inherit;cursor:pointer;border-radius:6px}
.specchip{border:1px solid var(--line,#d8d4cb);background:transparent;font:inherit;
  font-size:11px;padding:3px 10px;border-radius:12px;cursor:pointer;color:var(--slate,#6a655c)}
.specchip:hover{border-color:var(--clay,#7B1420)}
.specchip.on{background:var(--clay,#7B1420);color:#fff;border-color:var(--clay,#7B1420)}
#specClose{border:1px solid var(--line,#d8d4cb);background:transparent;font-size:15px;
  width:30px;height:30px;border-radius:6px;cursor:pointer;color:inherit}
#specClose:hover{border-color:var(--clay,#7B1420);color:var(--clay,#7B1420)}
.specbody{flex:1;overflow-y:auto;padding:18px}
.specgrid{display:grid;grid-template-columns:repeat(auto-fill,minmax(280px,1fr));
  gap:16px;max-width:1400px;margin:0 auto}
.speccard{border:1px solid var(--line,#d8d4cb);border-radius:10px;background:var(--card,#fff);
  overflow:hidden;display:flex;flex-direction:column;position:relative;cursor:pointer;
  transition:box-shadow .12s,border-color .12s}
.speccard:hover,.speccard:focus-visible{border-color:var(--clay,#7B1420);
  box-shadow:0 3px 14px rgba(0,0,0,.09);outline:none}
.speccard .cardexp{position:absolute;top:7px;right:7px;z-index:3;font-size:12px;
  width:22px;height:22px;border-radius:5px;background:rgba(20,18,16,.5);color:#fff;
  display:flex;align-items:center;justify-content:center;opacity:0;transition:opacity .12s}
.speccard:hover .cardexp,.speccard:focus-visible .cardexp{opacity:1}
/* detail lightbox: full card blown up */
.speclight .slcard{background:var(--card,#fff);border-radius:12px;max-width:min(920px,94vw);
  max-height:90vh;overflow:auto;box-shadow:0 12px 50px rgba(0,0,0,.45);cursor:default}
.speclight .slimgs{display:grid;gap:1px;background:var(--soft,#eee);
  grid-template-columns:repeat(auto-fit,minmax(300px,1fr))}
.speclight .slimg{margin:0;background:#fff;padding:18px;display:flex;flex-direction:column;
  align-items:center;gap:8px}
.speclight .slimg img{max-width:100%;max-height:60vh;object-fit:contain}
.speclight .slimg figcaption{font-size:10px;text-transform:uppercase;letter-spacing:.05em;
  color:var(--slate,#8a857c)}
.speclight .slnoimg{padding:40px;text-align:center;color:var(--slate,#b0aaa0);background:#fff}
.speclight .slmeta{padding:18px 22px}
.speclight .slcode{font-family:ui-monospace,Menlo,monospace;font-size:20px;font-weight:600;
  color:var(--clay,#7B1420)}
.speclight .slcat{font-size:12px;color:var(--slate,#8a857c);margin:3px 0 14px}
.speclight .sldims{display:grid;grid-template-columns:repeat(auto-fit,minmax(90px,1fr));gap:12px}
.speclight .sldl{display:flex;flex-direction:column;gap:2px}
.speclight .sldl span{font-size:10px;text-transform:uppercase;letter-spacing:.04em;
  color:var(--slate,#8a857c)}
.speclight .sldl b{font-size:15px}
.speclight .slmeta .sldl:last-child{margin-top:12px}
.speccard .specimgs{display:grid;gap:1px;background:var(--soft,#eee);
  border-bottom:1px solid var(--soft,#eee)}
.speccard .specimgs.one{grid-template-columns:1fr}
.speccard .specimgs.two{grid-template-columns:1fr 1fr}
.speccard .specimgs.none{padding:22px;text-align:center;color:var(--slate,#b0aaa0);
  font-size:12px;background:#fff}
.specthumb{position:relative;border:0;padding:8px;background:#fff;
  display:flex;align-items:center;justify-content:center;min-height:130px;width:100%}
.specthumb img{max-width:100%;max-height:170px;object-fit:contain;pointer-events:none}
.specthumb .thumbtag{position:absolute;top:5px;left:5px;font-size:9px;
  background:rgba(0,0,0,.06);color:var(--slate,#6a655c);padding:1px 6px;border-radius:3px;
  text-transform:uppercase;letter-spacing:.04em;z-index:2}
.speclight{position:fixed;inset:0;z-index:60;background:rgba(20,18,16,.82);
  display:flex;align-items:center;justify-content:center;padding:32px;cursor:zoom-out}
.slclose{position:absolute;top:18px;right:20px;background:rgba(255,255,255,.12);
  border:1px solid rgba(255,255,255,.3);color:#fff;font-size:16px;width:36px;height:36px;
  border-radius:8px;cursor:pointer}
.slclose:hover{background:rgba(255,255,255,.24)}
.speccard .specmeta{padding:11px 13px}
.speccard .spccode{font-family:ui-monospace,Menlo,monospace;font-size:14px;font-weight:600;
  color:var(--clay,#7B1420)}
.speccard .spccat{font-size:11px;color:var(--slate,#8a857c);margin:2px 0 8px;line-height:1.35}
.speccard .spcdims{display:flex;gap:12px;font-size:12px;flex-wrap:wrap}
.speccard .spcdims b{font-weight:600}
.speccard .spcdims .dl{color:var(--slate,#8a857c);margin-right:3px}
.speccard .spcprice{margin-top:8px;font-size:12px;color:var(--slate,#6a655c)}
.specempty{grid-column:1/-1;text-align:center;color:var(--slate,#8a857c);
  font-size:13px;padding:40px}
/* openFillerCard()'s card went at ui-0.66.0 and its styling went with it at
   ui-0.85.0: .crmod/.fcut/.fcutn/.crcut/.fmv/.fat/.crhead/.crtag/.crrow. The
   filler's cut width is the cabinet menu's now (.cabmenu .cmat). `.crnote`
   below is NOT part of that — the class is live in the Ideas panel. */
.cabpop .crnote{font-size:9px;color:var(--slate);font-style:italic;margin-top:4px;line-height:1.3}
#ctrwin{position:absolute;top:44px;right:10px;z-index:6;font-size:11px;
  padding:6px 10px;border:1px solid var(--clay);background:rgba(255,255,255,.95);
  border-radius:5px;cursor:pointer;color:var(--clay);font-weight:600}
#ctrwin:hover{background:var(--clay);color:#fff}
#ctrwinpop{position:absolute;top:76px;right:10px;z-index:7;background:#fff;
  border:1px solid var(--line);border-radius:9px;box-shadow:0 10px 30px rgba(40,36,32,.2);
  padding:5px;min-width:190px;display:flex;flex-direction:column;gap:2px}
#ctrwinpop .ctxh{font-size:9px;text-transform:uppercase;letter-spacing:.06em;
  color:var(--slate);font-weight:700;padding:5px 8px 3px}
#ctrwinpop button{text-align:left;font-size:12px;padding:7px 9px;border:none;
  background:none;border-radius:6px;cursor:pointer;color:var(--ink);font-family:var(--mono)}
#ctrwinpop button:hover{background:#f2efe8}
#ctrwinpop[hidden]{display:none!important}
/* In the canvas toolbar since ui-0.57.0; it floated on the plan before. */
#logmenu{position:relative;z-index:60}
#logbtn.off{opacity:.55}
#logbtn #traceCount{font-family:'Roboto Mono',monospace;font-size:10px;
  background:var(--line);color:var(--ink);border-radius:8px;padding:0 5px;margin-left:3px}
/* Fixed and placed by _place() in app.js - never right:0 off its button again.
   z-index with the other menus (.roommenu), above the tool rail and canvas. */
#logpop{position:fixed;z-index:1200;overflow:auto;background:#fff;border:1px solid var(--line);
  border-radius:8px;box-shadow:0 8px 24px rgba(40,36,32,.16);padding:5px;
  display:flex;flex-direction:column;gap:2px;min-width:140px}
#logpop button{text-align:left;font-size:12px;padding:7px 9px;border:none;background:none;
  border-radius:6px;cursor:pointer;color:var(--ink)}
#logpop button:hover{background:#f2efe8}
#logpop[hidden]{display:none!important}
/* NO CURSOR HERE (ui-0.84.0). It was `crosshair` for the whole canvas with
   `.grab`, `.grabbing` and `.placing` bolted on top, which could say four
   things and had to say them from two places — a stylesheet and eleven
   classList calls scattered through the handlers. `applyPlanCursor()` owns it
   now and sets it inline, so the table of what each tool looks like is one
   switch in `planCursor()` and there is nothing here to disagree with it.
   Default, not crosshair: a plan is a drawing to read as well as one to edit,
   and a canvas that is crosshair everywhere says everything is a target. */
#plan{display:block;width:100%;height:100%;cursor:default;touch-action:none;
  position:absolute;inset:0;z-index:2}
#plan[hidden]{display:none!important}
#view[hidden]{display:none!important}
.viewbar{display:flex;align-items:center;gap:12px;padding:8px 14px;flex-wrap:wrap;
  border-bottom:1px solid var(--line);background:var(--card);flex-shrink:0}
.seg2{display:flex;border:1px solid var(--line);border-radius:3px;overflow:hidden}
.seg2 button{border:0;border-radius:0;background:#fff;color:var(--slate);
  font-size:12px;font-weight:600;padding:6px 14px}
.seg2 button+button{border-left:1px solid var(--line)}
.seg2 button.on{background:var(--ink);color:var(--paper)}
.seg2 button:hover:not(.on){background:var(--sage);color:#fff}
.vhint{font-size:14px;color:var(--slate);cursor:help;user-select:none}
.vtools{margin-left:auto;display:flex;gap:5px;flex-wrap:wrap;justify-content:flex-end}
#view{position:absolute;inset:0;min-height:0;z-index:1}
#view canvas{display:block;width:100%;height:100%}
.vtools button{padding:5px 10px;font-size:11px}

.fit{padding:8px 11px;border-radius:3px;font-size:11.5px;line-height:1.45;margin-bottom:12px}
.fit.ok{background:rgba(74,122,82,.09);border-left:3px solid var(--ok);color:#33563a}
.fit.bad{background:rgba(180,100,60,.09);border-left:3px solid var(--warn);color:#7d4326}
.fit p+p{margin-top:5px}

.big{font-family:var(--display);font-size:31px;font-weight:700;letter-spacing:-.03em;line-height:1}
.ordtop{display:flex;align-items:center;justify-content:space-between;gap:10px;margin:2px 0}
.pricectl{display:flex;align-items:center;gap:5px;font-size:11px;color:var(--slate)}
.pricectl select,.pricectl input{font-family:var(--mono);font-size:12px;padding:3px 5px;border:1px solid var(--line);border-radius:6px;background:#fff}
.pricectl input{width:52px}
.sub{font-size:11px;color:var(--slate);margin-bottom:13px}
.list{font-family:var(--display);font-size:14px;color:var(--slate);
  text-decoration:line-through;text-decoration-color:var(--line);margin-bottom:2px}

table{width:100%;border-collapse:collapse;font-size:11.5px}
th{text-align:left;font-size:9px;letter-spacing:.07em;text-transform:uppercase;
  color:var(--slate);font-weight:600;padding:0 5px 5px 0;border-bottom:1px solid var(--line)}
td{padding:5px 5px 5px 0;border-bottom:1px solid var(--soft);vertical-align:top}
td.n,th.n{text-align:right;padding-right:0;font-family:var(--mono);font-size:11px}
.sku{font-family:var(--mono);font-weight:500}
.dsc{font-size:9.5px;color:var(--slate);text-transform:capitalize}
/* a cut note is a measurement, not a catalog name: "cut to 5"", never "Cut To 5"" */
.dsc.ocut{text-transform:none}
tr.tot td{border-bottom:0;border-top:2px solid var(--ink);padding-top:8px;
  font-weight:700;font-family:var(--display);font-size:13px}

.note{font-size:10.5px;color:var(--slate);line-height:1.5;margin-top:11px;
  padding-top:10px;border-top:1px solid var(--soft)}
.sw{display:flex;gap:4px;flex-wrap:wrap;margin-top:2px}
.sw button{width:26px;height:26px;padding:0;border-radius:3px;border:2px solid transparent;
  box-shadow:inset 0 0 0 1px rgba(0,0,0,.14)}
.sw button.on{border-color:var(--ink)}
.sw button.nosample{width:auto;min-width:26px;padding:0 7px;font-size:11px;color:var(--ink);
  border:1px solid #c9ccd0;text-shadow:0 0 2px #fff}
.sw button.nosample.on{border:2px solid var(--ink)}
.sw button:hover{border-color:var(--sage)}
.sug{border:1px solid var(--soft);border-left:3px solid var(--sage);
  border-radius:3px;padding:9px 11px;margin-bottom:8px;background:#fff}
.sug.fix{border-left-color:var(--warn)}
.sug .t{font-size:12.5px;font-weight:600;line-height:1.4;margin-bottom:4px}
.sug .y{font-size:11px;color:var(--slate);line-height:1.45;margin-bottom:7px}
.sug button{padding:4px 10px;font-size:11px}
.notes{font-size:11.5px;color:var(--slate);line-height:1.6;
  background:rgba(125,139,116,.09);border-left:3px solid var(--sage);
  padding:9px 11px;border-radius:3px;margin-bottom:12px}
.notes b{color:var(--ink);display:block;margin-bottom:4px;font-size:11px;
  text-transform:uppercase;letter-spacing:.06em;font-family:var(--display)}
.notes li{margin-left:14px;margin-bottom:2px}
.pick{margin-bottom:11px}
.pick .tabs{margin-bottom:0}
.tabs button .cnt{display:inline-block;margin-left:5px;padding:0 4px;
  border-radius:7px;background:rgba(0,0,0,.10);font-size:9.5px;
  font-family:var(--mono);min-width:14px}
.tabs button.on .cnt{background:rgba(255,255,255,.22)}

.trimhd{font-size:9px;text-transform:uppercase;letter-spacing:.08em;
  color:var(--slate);font-weight:800;margin-bottom:6px}
.trimbtn{flex:1;font-size:11px;padding:4px 8px;border:1px solid var(--line);
  background:#fff;border-radius:5px;cursor:pointer;color:var(--slate)}
.trimbtn:hover{border-color:var(--clay);color:var(--clay)}
.trimbtn.on{background:var(--clay);color:#fff;border-color:var(--clay)}
.sugrow{display:flex;align-items:center;gap:8px;padding:5px 2px;font-size:12px;
  border-top:1px solid var(--line)}
.sugsec{display:flex;align-items:center;gap:8px;margin-top:10px;padding-top:8px;
  border-top:1px solid var(--line)}
.sugsec:first-of-type{margin-top:4px;border-top:0;padding-top:0}
.sugseclab{font-size:11px;font-weight:700;color:var(--slate);margin-right:auto}
.sugsku{font-family:var(--mono);font-weight:700;font-size:11.5px}
.sugwhere{color:var(--slate);font-size:11px;margin-left:auto;padding-right:6px}
.addbox{background:transparent;border:0;border-radius:0;padding:0;margin:0}
.addbox .add{margin:0 0 6px}
.addbox .add:last-child{margin:0}
.usebar{margin:4px 0 12px}
.usebar .track{height:5px;background:var(--soft);border-radius:3px;overflow:hidden}
.usebar .used{height:100%;background:var(--sage);transition:width .15s}
.usebar .used.over{background:var(--warn)}
.usebar .nums{display:flex;justify-content:space-between;margin-top:4px;
  font-family:var(--mono);font-size:10px;color:var(--slate)}
.usebar .nums .warn{color:var(--warn);font-weight:600}

.it{display:block;padding:7px 9px;background:#fff;border:1px solid var(--soft);
  border-radius:3px;margin-bottom:5px;font-size:12px;cursor:pointer}
.it:hover{border-color:var(--sage)}
/* A SELECTED row. The tint is the token's own colour at 6%, not the old
   rgba(180,100,60,.05) — that was a third colour nobody could name, and a
   selected row should read as the same decision as the tab above it. */
.it.sel{border-color:var(--ksp-selection);border-width:2px;padding:6px 8px;
  background:rgba(107,36,52,.06)}
.it .ihead{display:flex;align-items:center;gap:7px;flex-wrap:wrap}
.it .ihead .c{font-weight:600}
.winflds{display:flex;flex-wrap:wrap;gap:6px;margin-top:6px}
.winflds label{display:inline-flex;align-items:center;gap:3px;font-size:10px;color:var(--slate);font-family:var(--mono)}
.winflds input{width:44px;font-family:var(--mono);font-size:11px;padding:2px 4px;border:1px solid var(--line);border-radius:5px}
.ctxmenu{position:fixed;z-index:1001;/* above #cabpill (60): the stool's Delete sat under it (job 1017-2238-s8) */background:#fff;border:1px solid var(--line);border-radius:9px;box-shadow:0 10px 30px rgba(40,36,32,.2);padding:5px;min-width:170px;display:flex;flex-direction:column;gap:2px}
.ctxmenu .ctxh{font-size:9px;text-transform:uppercase;letter-spacing:.06em;color:var(--slate);font-weight:700;padding:5px 8px 3px}
.ctxmenu button{text-align:left;font-size:12px;padding:7px 9px;border:none;background:none;border-radius:6px;cursor:pointer;color:var(--ink);font-family:var(--mono)}
.ctxmenu button:hover{background:#f2efe8}
.ctxmenu .ctxnote{font-size:11px;color:var(--slate);padding:7px 9px;max-width:210px;line-height:1.35}
.ctxmenu .ctxsep{height:1px;background:var(--line);margin:3px 4px}
.ctxmenu button.ctxdanger{color:#b3261e}
.ctxmenu button.ctxdanger:hover{background:#fbeceb}
/* cabRow()'s own controls — .athd / .iacts / .ipos / .rqrow / .mv / .w and
   the .ap row variant — went at ui-0.85.0 with the appliance card that built
   them. Two things next to them did NOT: `.it` itself, because windowRow()
   still builds one (Windows panel, and inside .cabpop.winpop), and `.it.sel`
   above, which is a named consumer of --ksp-selection and is still cleared by
   paintSelOnly(). */
.cabpop{position:fixed;z-index:1001;overflow-y:auto;overscroll-behavior:contain;background:#fff;border:1px solid var(--line);
  border-radius:8px;box-shadow:0 10px 32px rgba(40,36,32,.28);padding:8px;
  width:250px;max-width:90vw}
.cabpop .it{margin:0}
/* Corner ✕ removes the cabinet. Tinted red so it doesn't read as a plain
   "dismiss" — this is a destructive action sitting where a close button lives. */
.it{position:relative}
/* ✕ closes the card (neutral). The 🗑 beside it is the destructive remove, so a
   reflexive click on the corner can't delete a cabinet. */
.cardx{position:absolute;top:5px;right:5px;width:20px;height:20px;padding:0;
  border:1px solid var(--line);background:#f4f3f0;color:var(--slate);border-radius:5px;
  font-size:11px;line-height:1;cursor:pointer;display:flex;align-items:center;
  justify-content:center;z-index:2}
.cardx:hover{background:#e7e5e0;color:var(--ink)}
.cardrm{position:absolute;top:5px;right:29px;width:20px;height:20px;padding:0;
  border:1px solid #e6c9c9;background:#fceaea;color:#A32D2D;border-radius:5px;
  font-size:10px;line-height:1;cursor:pointer;display:flex;align-items:center;
  justify-content:center;z-index:2}
.cardrm:hover{background:#A32D2D;color:#fff;border-color:#A32D2D}
.it .ihead{padding-right:56px}
.rotpop{position:fixed;z-index:1400;overflow-y:auto;overscroll-behavior:contain;background:#fff;border:1px solid var(--line);
  border-radius:6px;box-shadow:0 8px 28px rgba(40,36,32,.22);padding:10px;width:150px}
.rotpop .rphd{font-size:10px;text-transform:uppercase;letter-spacing:.06em;
  color:var(--slate);margin-bottom:7px}
.rotpop .rpv{color:var(--clay);font-family:var(--mono);margin-left:4px}
.rotpop .rpdial{display:flex;justify-content:center;margin-bottom:8px}
.rotpop .rpdial canvas{cursor:pointer;touch-action:none}
.rotpop .rprow{display:flex;gap:4px;align-items:center;margin-top:5px}
.rotpop .rprow button{flex:1;font-size:10px;padding:3px 2px;cursor:pointer;
  border:1px solid var(--line);background:transparent;border-radius:3px;color:var(--slate)}
.rotpop .rprow button:hover{border-color:var(--clay);color:var(--clay)}
.rotpop .rpnum{width:52px;padding:3px 5px;font-size:11px;font-family:var(--mono)}
.rotpop .rprow label{font-size:9px;text-transform:uppercase;color:var(--slate)}
.rotpop .rpnote{font-size:9px;color:var(--slate);font-style:italic;justify-content:center;margin-top:6px}
.it .c{font-family:var(--mono);font-weight:500}
.it .x{background:transparent;border:0;color:var(--line);font-size:15px;
  line-height:1;padding:0 2px;cursor:pointer}
.it .x:hover{color:var(--warn);background:transparent}
.startrow{display:flex;align-items:center;gap:5px;margin:9px 0 4px;
  padding-top:9px;border-top:1px solid var(--soft)}
.startrow label{font-size:9.5px;text-transform:uppercase;letter-spacing:.06em;
  color:var(--slate);flex-shrink:0}
.startrow input{width:62px;padding:3px 5px;font-size:11px;font-family:var(--mono)}
.startrow .u{font-size:10px;color:var(--slate)}
/* --- panel-level workspace tabs + Room accordion (ui-0.17.26) --- */
/* Panel chrome: sits above .pb and never scrolls with it. flex-shrink:0 or a
   long content pane squeezes it — flex items shrink by default, and a tab bar
   that loses four pixels per section is worse than one that scrolls. */
.ptabbar{display:flex;gap:3px;padding:8px;border-bottom:1px solid var(--line);
  background:var(--card);flex:0 0 auto}
.ptab{flex:1;height:32px;border:1px solid transparent;background:transparent;color:var(--slate);border-radius:7px;font-size:13px;cursor:pointer;font-family:var(--body)}
.ptab:hover{background:var(--soft);color:var(--ink)}
.ptab.on{background:var(--ksp-selection);color:#fff;font-weight:600;border-color:var(--ksp-selection)}
.papp{display:flex;align-items:center;justify-content:space-between;padding:7px 12px;border-bottom:1px solid var(--line);font-size:12px;color:var(--slate)}
.papp .grp{display:flex;gap:7px;align-items:center;cursor:pointer}
.papp .grp:hover{color:var(--ink)}
.rdbody{}
.acc{border-bottom:1px solid var(--soft)}
.acchead{display:flex;align-items:center;justify-content:space-between;padding:13px 14px;cursor:pointer;user-select:none}
.acchead:hover{background:var(--soft)}
.acchead .ttl{display:flex;align-items:center;gap:8px;color:var(--ink);font-size:14px;font-weight:600}
.acchead .val{font-size:12px;color:var(--slate)}
.acchead .caret{color:var(--slate);transition:transform .12s ease}
.acc.closed .acchead .caret{transform:rotate(-90deg)}
.accbody{padding:2px 14px 16px}
/* The Color row (ui-1.13.0): a button that shows the colour AS a colour,
   and a list with a swatch per row. Same shapes as the selects above it. */
/* The app's generic <button> is dark navy; this one has to read as the
   fourth control in a column of three white selects. */
.catcolbtn{display:flex;align-items:center;gap:9px;text-align:left;cursor:pointer;
  background:#fff;color:var(--ink);font:inherit;font-size:13px}
.catcolbtn:hover{border-color:#c2b8a6}
/* It keeps looking like a control after it is clicked: the app's generic
   button turns dark on focus, which on this one hid the colour it is for. */
.catcolbtn:focus,.catcolbtn:active{background:#fff;color:var(--ink)}
.catcolbtn:focus-visible{outline:2px solid var(--ksp-selection);outline-offset:1px}
.catcolbtn:disabled{opacity:.55;cursor:default}
.catcolbtn .catcolname{flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;
  white-space:nowrap}
.catcolbtn .catcolcar{color:var(--slate);font-size:11px}
.kspdlgsub{margin-top:8px;font-size:12px;color:var(--slate)}
.catcolnote{margin-top:5px;font-size:11.5px;line-height:1.4;color:#b4643c}
.catcolmenu{max-height:340px;overflow-y:auto}
.catcolmenu .catsw, .catcolbtn .catsw { flex:0 0 auto; margin-left:0 }
/* The colour button's own chip: a fixed square, so it shows the colour (or the
   "no sample yet" hatch) rather than collapsing to a sliver beside the name. */
.catcolbtn .catsw {width:18px;min-width:18px;height:18px;display:inline-block}
/* "no sample yet": small, beside the name, never mistaken for the name. */
.catnosample{font-size:10.5px;color:var(--slate);font-style:italic;white-space:nowrap}
/* Over the 3D view and the elevation while any finish is drawn as the neutral
   placeholder (layout.js paintNoSampleNote). */
#nosampleNote{position:absolute;left:12px;bottom:12px;z-index:6;max-width:min(520px,70%);
  background:rgba(255,255,255,.94);color:var(--ink);border:1px solid var(--line);
  border-left:4px solid #9aa0a6;border-radius:8px;padding:7px 11px;font-size:12px;
  line-height:1.35;box-shadow:0 4px 14px rgba(20,26,34,.12);pointer-events:none}
#nosampleNote[hidden]{display:none}

/* THE CAMERA PRESET BAR (ui-1.15.0): two room views and one per wall,
   over the top-left of the 3D canvas where it does not cover the room. */
#cambar{position:absolute;top:10px;left:10px;z-index:6;display:flex;flex-wrap:wrap;
  align-items:center;gap:4px;max-width:calc(100% - 20px);
  background:rgba(255,255,255,.93);border:1px solid var(--line);border-radius:9px;
  padding:5px 6px;box-shadow:0 4px 14px rgba(20,26,34,.12)}
#cambar .cbtn{height:26px;padding:0 10px;border:1px solid var(--line);border-radius:6px;
  background:#fff;color:var(--slate);font:inherit;font-size:12px;cursor:pointer;
  white-space:nowrap}
#cambar .cbtn:hover{background:var(--soft);color:var(--ink)}
#cambar .cbtn.on{background:var(--ksp-selection);border-color:var(--ksp-selection);color:#fff}
#cambar .cbsep{width:1px;height:18px;background:var(--line);margin:0 3px}

/* ===== THE DETAILS BOX (ui-1.9.0) =========================================
   Floats over the drawing beside the cabinet it describes. ~480px, draggable
   by its bar, one at a time. It is NOT part of the right panel and must not
   push it around — that is the whole point of it. */
#cabDetails{position:absolute;z-index:9;width:480px;max-width:calc(100% - 24px);
  max-height:calc(100% - 24px);display:flex;flex-direction:column;
  background:var(--card);border:1px solid var(--line);border-radius:10px;
  box-shadow:0 14px 44px rgba(20,26,34,.28);overflow:hidden}
#cabDetails .cdbar{display:flex;align-items:center;gap:8px;padding:9px 10px 9px 14px;
  background:var(--ksp-selection);color:#fff;cursor:grab;user-select:none;flex:0 0 auto}
#cabDetails.dragging .cdbar{cursor:grabbing}
#cabDetails .cdttl{flex:1;min-width:0;font-size:13.5px;font-weight:600;
  white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
#cabDetails .cdx{width:24px;height:24px;border:0;border-radius:6px;background:transparent;
  color:#fff;cursor:pointer;font-size:13px;line-height:1}
#cabDetails .cdx:hover{background:rgba(255,255,255,.18)}
#cabDetails #psel{flex:1;min-height:0;overflow-y:auto;padding:12px 14px 16px}
/* a panel's Remove stays in view while the card scrolls in a short box */
#cabDetails #psel .selbtns:has(#selSkinRemove){position:sticky;bottom:-16px;background:var(--card);padding:8px 0 16px}
/* The drawing is the reason the box is 480 wide: it has to be READ, not
   glanced at. In the panel it was capped at 112px; here it gets the width,
   and a click opens the full spec card. */
#cabDetails .selimg{max-height:260px;width:auto;cursor:zoom-in}
#cabDetails svg.selimg{height:200px;cursor:default}

/* ===== THE CABINET BROWSER (ui-1.1.0) =====================================
   Over the right of the drawing, ~820px at a 1440 window and never wider
   than the canvas it covers. The collapse has NO transition on width: Nick
   asked for instant, and an animated 820px slide is exactly the delay that
   makes you wait to see where you are dropping. */
#cabBrowser{position:absolute;top:0;right:0;bottom:0;z-index:7;display:flex;
  width:min(820px,80%);background:var(--card);border-left:1px solid var(--line);
  box-shadow:-14px 0 34px rgba(20,26,34,.13)}
#cabBrowser.collapsed{width:34px;box-shadow:none}
#cabBrowser .cbstrip{display:none}
#cabBrowser .cbstripx{display:none}
#cabBrowser.collapsed .cbstrip{display:flex;align-items:center;justify-content:center;
  flex:1;background:var(--ksp-selection);color:#fff;font-size:11px;letter-spacing:.06em;
  writing-mode:vertical-rl;text-orientation:mixed;padding:10px 0;user-select:none}
#cabBrowser.collapsed .cbmain{display:none}
.cbmain{flex:1;display:flex;flex-direction:column;min-width:0;min-height:0}
.cbhead{display:flex;align-items:center;gap:10px;padding:12px 14px;
  border-bottom:1px solid var(--line);background:var(--card);color:var(--ink)}
.cbhead h2{margin:0;font-size:15px;font-weight:700;color:var(--ink)}
#cbSearch{flex:1;min-width:0;height:30px;padding:0 10px;border:1px solid var(--line);
  border-radius:7px;background:#fff;font:inherit;font-size:13px;color:var(--ink)}
.cbscope{flex:0 0 auto;width:auto;max-width:150px;text-overflow:ellipsis;height:30px;padding:0 6px;border:1px solid var(--line);
  border-radius:7px;background:#fff;font:inherit;font-size:12.5px;color:var(--ink)}
.cbscope.narrowed{border-color:var(--ksp-selection);background:#eef3fb;font-weight:600}
.cbscope[hidden]{display:none}
.cbcat{display:flex;flex-wrap:wrap;align-items:flex-end;gap:6px 10px;padding:4px 0 6px}
.cbcat[hidden]{display:none}
.cbcatf{display:flex;flex-direction:column;font-size:11px;color:var(--muted,#667)}
.cbcatf select{height:30px;max-width:170px;padding:0 6px;border:1px solid var(--line);border-radius:6px;background:var(--bg,#fff);font-size:13px}
.cbcatnote{flex:1 1 100%;font-size:11px;color:var(--muted,#667)}
.cbstatus{display:flex;align-items:center;gap:8px;padding:6px 14px;font-size:12px;
  color:var(--slate);border-bottom:1px solid var(--line);background:#f6f3ee}
.cbstatus[hidden]{display:none}
.cbstatus .cbchip{height:22px;padding:0 9px;border:1px solid var(--line);border-radius:11px;
  background:#fff;color:var(--ink);font-size:11.5px;cursor:pointer}
.cbgroup{grid-column:1/-1;font-size:11px;text-transform:uppercase;letter-spacing:.06em;
  color:var(--slate);font-weight:700;padding:6px 2px 0;border-top:1px solid var(--line)}
.cbgroup:first-child{border-top:0;padding-top:0}
.cbico{flex:0 0 auto;width:30px;height:30px;border:1px solid var(--line);border-radius:7px;
  background:#fff;color:var(--slate);cursor:pointer;font-size:14px;line-height:1;
  padding:0;display:inline-flex;align-items:center;justify-content:center}
/* the ✕ glyph rides ~1.25px high in its line box; 2px of top padding lowers the centred glyph by 1px */
#cbClose{padding-top:2px}
.cbico:hover{background:var(--soft);color:var(--ink)}
.cbico[aria-pressed="true"]{background:var(--ksp-selection);border-color:var(--ksp-selection);color:#fff}
.cbfits{padding:10px 14px;border-bottom:1px solid var(--line);background:#f6f3ee}
.cbfits .cbfl{font-size:11px;text-transform:uppercase;letter-spacing:.06em;
  color:var(--slate);margin-bottom:6px}
.cbfits .cbfr{display:flex;gap:6px;overflow-x:auto;padding-bottom:2px}
/* CHIP ROWS WRAP (Nick, 2026-10-03, replacing his earlier "chips never
   wrap"): in the floating box a one-line row hid most of its chips behind a
   sideways scrollbar, which is worse than a second line. */
.cbchips{display:flex;flex-wrap:wrap;gap:6px;
  padding:8px 14px;border-bottom:1px solid var(--line)}
.cbchips[hidden]{display:none}
.cbchips .cbchip{flex:0 0 auto;height:26px;padding:0 11px;border:1px solid var(--line);
  border-radius:13px;background:#fff;color:var(--slate);font:inherit;font-size:12px;
  cursor:pointer;white-space:nowrap}
.cbchips .cbchip:hover{background:var(--soft);color:var(--ink)}
.cbchips .cbchip.on{background:var(--ksp-selection);border-color:var(--ksp-selection);color:#fff}
.cbgrid{flex:1;min-height:0;overflow-y:auto;overflow-x:hidden;display:grid;gap:10px;padding:12px 14px;
  grid-template-columns:repeat(auto-fill,minmax(104px,1fr));align-content:start}
.cbcard{min-width:0;border:1px solid var(--line);border-radius:9px;background:#fff;padding:8px;
  display:flex;flex-direction:column;gap:6px;cursor:grab;user-select:none}
.cbcard:hover{border-color:var(--ksp-selection)}
.cbcard.dragging{opacity:.45;cursor:grabbing}
.cbcard .cbdraw{background:#faf9f7;border-radius:6px;aspect-ratio:1/1;
  display:flex;align-items:center;justify-content:center;max-width:100%}
.cbcard .cbdraw svg{width:100%;height:100%;display:block}
.cbcard .cbsku{overflow-wrap:anywhere;font-family:var(--mono,ui-monospace,monospace);font-size:12px;font-weight:700;color:var(--ink)}
.cbcard .cbw{font-size:11px;color:var(--ksp-selection);font-weight:600}
.cbcard .cbdesc{font-size:10.5px;line-height:1.35;color:var(--slate)}
.cbnote{grid-column:1/-1;color:var(--slate);font-size:12.5px;padding:6px 2px}
/* THE PICKER'S SIZE BOXES (picker.js): Width for a window or door, Height
   for a window, one row between the chips and the cards. The armed card is
   outlined in the accent. */
.cbopts{display:flex;flex-wrap:wrap;align-items:center;gap:8px 14px;
  padding:8px 14px;border-bottom:1px solid var(--line);font-size:12px;color:var(--slate)}
.cbopts[hidden]{display:none}
.cbopts .pkfield{display:flex;align-items:center;gap:6px}
.cbopts input{width:64px;height:26px;padding:0 7px;border:1px solid var(--line);
  border-radius:6px;background:#fff;font:inherit;font-size:12.5px;color:var(--ink)}
.cbopts input.bad{border-color:#c0392b;color:#c0392b}
.cbopts .pknote{font-size:11px;opacity:.85}
.cbcard.on{border-color:var(--ksp-selection);box-shadow:0 0 0 2px var(--ksp-selection) inset}
/* The floating Cabinets(n) badge must never cover the browser or its cards.
   As the flyout the browser is on the LEFT, so the badge stays where it is. */
#stage.cbopen:not(.cbfly) #cablegend,#stage.cbopen:not(.cbfly) #cabpill{right:calc(min(820px,80%) + 12px)}
#stage.cbopen.cbdrag:not(.cbfly) #cablegend,#stage.cbopen.cbdrag:not(.cbfly) #cabpill{right:46px}
/* THE FLYOUT (2026-10-02): a floating box beside the rail instead of the wide
   panel. browser.js sets left/top/width/height inline and keeps them inside
   #stage, which is what keeps it off the right-hand panel. The corner handle
   is our own handles, not `resize:both`, because a touch screen gets the
   flyout too and no browser hands the native handle to a finger. The header
   and the grip take `touch-action:none` so a finger drags them rather than
   scrolling the page. Collapsed for a drag it is the same maroon strip,
   short, where the box was. */
#cabBrowser.flyout{right:auto;bottom:auto;width:680px;height:600px;
  min-width:360px;min-height:300px;max-width:calc(100% - 16px);max-height:calc(100% - 16px);
  border:1px solid var(--line);border-radius:10px;overflow:hidden;
  box-shadow:0 14px 44px rgba(20,26,34,.28)}
#cabBrowser.flyout .cbhead{cursor:grab;user-select:none;touch-action:none}
/* EVERY EDGE AND CORNER RESIZES, like an ordinary window (2026-10-03; the
   maroon hatched corner grip went 2026-10-05, Nick: "just make it as simple
   as possible"). Invisible strips along the edges and squares at the
   corners, all above the content, each with the normal resize cursor and
   nothing painted. */
#cabBrowser .cbrs{display:none}
#cabBrowser.flyout .cbrs{display:block;position:absolute;z-index:3;touch-action:none}
#cabBrowser.flyout .cbrs-n{top:0;left:12px;right:12px;height:6px;cursor:ns-resize}
#cabBrowser.flyout .cbrs-s{bottom:0;left:12px;right:12px;height:6px;cursor:ns-resize}
#cabBrowser.flyout .cbrs-e{right:0;top:12px;bottom:12px;width:6px;cursor:ew-resize}
#cabBrowser.flyout .cbrs-w{left:0;top:12px;bottom:12px;width:6px;cursor:ew-resize}
#cabBrowser.flyout .cbrs-nw{left:0;top:0;width:12px;height:12px;cursor:nwse-resize}
#cabBrowser.flyout .cbrs-ne{right:0;top:0;width:12px;height:12px;cursor:nesw-resize}
#cabBrowser.flyout .cbrs-sw{left:0;bottom:0;width:12px;height:12px;cursor:nesw-resize}
#cabBrowser.flyout .cbrs-se{right:0;bottom:0;width:12px;height:12px;cursor:nwse-resize}
/* THE GRIP SITS OUTSIDE THE SCROLLBARS (job 1022-2036, Nick: "those scrolling
   things on the right and bottom is i cannot use only expand the window"). The
   strips used to lie over the card list's scrollbar, so grabbing the thumb
   resized the window. The list stops 6px short of the right edge (clear of the
   east strip) and 12px short of the bottom (clear of the corner squares, so
   they do not cover the scrollbar's end either). Only the list moves: a rim
   round the whole box took 12px off the header and squeezed the search bar
   (batch-201, test_cab_search_scope). */
#cabBrowser.flyout .cbgrid{margin:0 6px 12px 0}
#cabBrowser.flyout.collapsed .cbrs{display:none}
#cabBrowser .cbresetbtn{width:auto;padding:0 9px;font-size:11.5px;white-space:nowrap}
#cabBrowser:not(.flyout) .cbresetbtn{display:none}
#cabBrowser.flyout.moving .cbhead{cursor:grabbing}
#cabBrowser.flyout.collapsed{width:34px!important;height:200px!important;
  min-width:0;min-height:0;resize:none;box-shadow:none}
/* The mid-drag hint, under the cursor's world: what a release does. */
#cbHint{position:absolute;left:50%;transform:translateX(-50%);bottom:18px;z-index:8;
  background:var(--ksp-selection);color:#fff;padding:7px 14px;border-radius:8px;
  font-size:12.5px;letter-spacing:.02em;box-shadow:0 6px 20px rgba(20,26,34,.25);
  pointer-events:none;white-space:nowrap}
/* Design > Cabinets: the two views of one catalog (ui-1.0.0). The segment is
   the same piece as the colour-scheme one; the cards keep the scroll height
   the Catalog tab gave them. */
/* The summary sits at the RIGHT, against the caret, like the Space
   accordion's — the title is flex and would otherwise glue them together. */
.dacc .acchead .val{margin-left:auto;padding-right:10px}
.cabseg{margin:0 0 8px}
.cabcards{max-height:none}
/* Design accordion headers: plain, no fill box (match Areas rows) */
.dacc .acchead{background:transparent;padding:12px 14px}
.dacc.acc{border-bottom:2px solid #d5dbe4}
.dacc .acchead:hover{background:#e3e8ef}
.dacc .acchead .ttl{font-size:14px;font-weight:600;color:#1b2430}
/* soft slate panel (#eef1f5) + heavier dividers (ui-0.17.28) */
#sideL{background:#eef1f5}
.pb{background:#eef1f5}
.papp{background:#e3e8ef;border-bottom:1px solid #cfd6e0;color:#4a5568}
.ptabbar{background:#e3e8ef;border-bottom:1px solid #cfd6e0}
.ptab{color:#5a6473}
.ptab:hover{background:#dbe1ea;color:#1b2430}
.ptab.on{background:var(--ksp-selection);color:#fff;border-color:var(--ksp-selection)}
.acc{border-bottom:2px solid #d5dbe4}
.acchead{background:transparent}
.acchead:hover{background:#e3e8ef}
.acchead .ttl{color:#1b2430}
.acchead .val{color:#5a6473}
/* The catalog picker's fields — label ABOVE a full-width control, so all four
   start and end at the same x (ui-0.73.2). See catalogPicker()'s mkRow for the
   defect this replaces. Declared here AND again with .catbox below; both say
   the same thing now, where they used to contradict each other on the row's
   margin. */
.catselrow{display:block;margin-bottom:9px}
.catselrow:last-of-type{margin-bottom:0}
.catlabrow{display:flex;align-items:center;gap:6px;margin-bottom:4px}
.catminilab{flex:0 0 auto;font-size:9.5px;font-weight:600;color:var(--slate);
  text-transform:uppercase;letter-spacing:.07em}
.catselrow .catsel{display:block;width:100%;min-width:0}
/* The swatch is pushed to the END of the label line, so it is in the same
   column as the labels and cannot displace the control under it. */
.catsw{width:16px;height:16px;flex:0 0 auto;margin-left:auto;border-radius:4px;
  border:1.5px solid #9aa6b6}
/* Areas nested room tree (ui-0.17.31) */
.area{border-bottom:2px solid #d5dbe4}
.areahead{display:flex;align-items:center;justify-content:space-between;padding:7px 10px;cursor:pointer;user-select:none}
.areahead:hover{background:#e3e8ef}

.areahead{padding:8px 12px}
.areahead .ttl{display:flex;align-items:center;gap:8px;font-size:13px;font-weight:500;background:var(--ksp-selection);color:#fff;padding:6px 11px;border-radius:7px;flex:1;min-width:0}
.areahead .rmname{flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.areatools{display:flex;align-items:center;gap:10px;margin-left:auto;color:#fff}
.rmedit,.rmdel,.areatools .caret{color:#fff}

.area:not(.open) .areabody{display:none}
.areatools{display:flex;align-items:center;gap:10px;color:#5a6473}
.rmedit,.rmdel{cursor:pointer;font-size:13px;opacity:.85;line-height:1}
.caret{font-size:11px;opacity:.9}
.rmedit:hover{opacity:1;color:#fff}
.rmdel:hover{opacity:1;color:#ffd1d1}
.areabody{background:#f4f6f9;padding:2px 0 4px 8px}
.areaadd{display:flex;align-items:center;gap:8px;padding:12px 14px;font-size:13px;color:var(--clay);cursor:pointer;font-weight:500}
.areaadd:hover{background:#e3e8ef}
/* section headers inside a room are a touch smaller */
.areabody .acchead .ttl{font-size:13px}
/* fancier segmented tabs (ui-0.17.30) */
.ptabbar{padding:10px;gap:0;background:#e3e8ef;border-radius:0}
#ptabs{background:#dbe1ea;border:1px solid #cfd6e0;border-radius:9px;padding:3px}
.ptab{border-radius:7px;height:36px;font-size:14px;transition:background .12s}
.ptab.on{box-shadow:0 1px 2px rgba(0,0,0,.12)}
/* potential issues section */
.issuesSec{border-top:2px solid #d5dbe4;margin-top:6px}
.issuesHead{display:flex;align-items:center;justify-content:space-between;padding:11px 14px;cursor:pointer;user-select:none}
.issuesHead:hover{background:#e3e8ef}
.issuesHead .ttl{font-size:13px;font-weight:600;color:#1b2430;display:flex;align-items:center;gap:8px}
.issuesCount{display:inline-flex;align-items:center;justify-content:center;min-width:20px;height:20px;padding:0 6px;border-radius:10px;background:#c9d2dd;color:#4a5568;font-size:11px;font-weight:600}
.issuesCount.has{background:var(--warn);color:#fff}
.issuesCount.blocking{background:#c98a0b;color:#fff}
/* order warnings (ui-0.65.0): amber, never a gate */
.ordwarn,.ordok{margin:8px 0 10px;padding:8px 10px;border-radius:6px;font-size:12px;line-height:1.45}
.ordwarn ul{margin:4px 0 6px 16px;padding:0}
.ordwarn{background:#fff4dc;border:1px solid #d99a1e;color:#5c3d05}
.ordwarn button{padding:2px 10px}
.ordok{background:#e8f3ea;border:1px solid #4a8a57;color:#1f4d2a}
/* THE ORDER PANEL — head / scrolling body / PINNED foot (ui-0.74.0), laid out
   to docs/design/crisp-blue/2-order-panel.png. Original colours.

   The panel is a flex column and only .ordbody scrolls, which is what keeps
   Subtotal and Dealer total on screen under a long order. .pb loses its
   padding and its own scroll for this one mode, or there would be two
   scrollers and the footer would sit inside the inner one. */
/* THE ORDER WINDOW (ui-1.16.0). #po is a window and nothing else now — it
   is a child of <main>, not of the panel, so opening the order leaves the
   right panel exactly as it was. It used to live in the panel with an
   "expanded" class that lifted it out; the class is gone with the panel view
   and these are the same numbers it carried. It sits above the Log menu and
   the objects pill (60/65) and under the two popups it opens itself, the
   issues list and Review (68). */
#po{position:fixed;z-index:66;top:56px;bottom:24px;left:max(24px,calc(50% - 560px));
  right:max(24px,calc(50% - 560px));overflow-y:auto;background:var(--card);
  border:1px solid var(--line);border-radius:10px;padding:16px 24px 28px;
  box-shadow:0 0 0 100vmax rgba(20,26,34,.35),0 12px 40px rgba(0,0,0,.25);font-size:13px;
  display:flex;flex-direction:column;min-height:0}
#po .ordhd .ttl{font-size:16px}
/* THE CLOSE BUTTON (ui-1.16.0): one control where there were three that
   overlapped — an expand icon, a ▷ minimize and the word "Collapse" written
   over the first. */
.ordclose{height:28px;padding:0 12px;border:1px solid var(--line);border-radius:7px;
  background:#fff;color:var(--ink);font:inherit;font-size:12.5px;font-weight:600;
  cursor:pointer;white-space:nowrap}
.ordclose:hover{background:var(--ksp-selection);border-color:var(--ksp-selection);color:#fff}
.ordhd{display:flex;align-items:flex-start;justify-content:space-between;gap:8px;
  flex-shrink:0;padding:12px 14px 9px;border-bottom:1px solid var(--soft)}
.ordhd .ttl{font-size:15px;font-weight:700;color:#1b2430;line-height:1.15}
.ordsub{font-size:11.5px;color:var(--slate);margin-top:2px}
.ordspace{font-size:11.5px;color:#7d4326;margin-top:4px;padding:3px 7px;border-left:3px solid var(--warn);background:rgba(180,100,60,.09)}
.ordacts{display:flex;align-items:center;gap:4px;flex-shrink:0}
.ordibtn{width:26px;height:26px;padding:0;border:1px solid transparent;
  background:transparent;color:var(--slate);border-radius:6px;cursor:pointer;
  font-size:13px;line-height:1;font-family:var(--body)}
.ordibtn:hover{background:var(--soft);color:var(--ink);border-color:var(--line)}
.ordbody{flex:1;min-height:0;overflow-y:auto;overflow-x:hidden;
  padding:10px 14px 14px;scrollbar-gutter:stable}
.ordfoot{flex-shrink:0;padding:10px 14px 12px;border-top:1px solid var(--line);
  background:var(--card)}
.ordtot{display:flex;align-items:baseline;justify-content:space-between;gap:10px;
  font-size:12px;color:var(--slate);padding:1px 0}
.ordtot b{font-family:var(--mono);font-variant-numeric:tabular-nums;
  font-size:12.5px;color:var(--ink)}
/* The on-screen totals: one grid, so the Shipping $ and Tax % boxes stand in one
   column and every amount (Subtotal to Total) right-aligns in another. */
.ordtots-grid{display:grid;grid-template-columns:minmax(0,1fr) 112px 92px;
  align-items:center;column-gap:8px;row-gap:3px;font-size:12px;color:var(--slate)}
.ordtot-row{display:contents}
.ordtot-input{display:flex;align-items:center;justify-content:flex-end;gap:4px}
.ordtot-input input{width:74px;box-sizing:border-box;text-align:right;font-family:var(--mono)}
.ordtot-input .ordunit{width:10px;text-align:center}
.ordtot-amount{text-align:right;font-family:var(--mono);font-variant-numeric:tabular-nums;
  font-size:12.5px;color:var(--ink)}
.ordtot-row.big .ordtot-label{font-size:13px;font-weight:600;color:var(--ink)}
.ordtot-row.big .ordtot-amount{font-size:16px;font-weight:700}
.ordtbl th.odisc,.ordtbl td.odisc{width:84px;text-align:right}
.ordtbl td.odisc{overflow:visible}
.odisclab{color:var(--slate);font-size:10px}
.ordedited{font-style:normal;font-size:10px;color:var(--accent,#2a6)}
.ordreset{padding:0 6px;font-size:10px}
.ordtot.big{font-size:13px;font-weight:600;color:var(--ink);margin-top:1px}
.ordtot.big b{font-size:16px;font-weight:700}
.ordbtns{display:grid;grid-template-columns:1fr 1.15fr;gap:8px;margin-top:9px}
.ordbtns button{padding:10px 8px;font-size:12.5px;border-radius:5px}
.ordbill{position:relative;display:block}
.ordbill>button{width:100%}
.ordbilllist{position:absolute;right:0;bottom:100%;margin-bottom:6px;min-width:100%;z-index:5;display:flex;flex-direction:column;gap:4px;padding:6px;background:var(--panel,#fff);border:1px solid var(--line,#ccd);border-radius:6px;box-shadow:0 4px 14px rgba(0,0,0,.25)}
.ordbilllist[hidden]{display:none}
.ordbilllist button[disabled]{opacity:.5;cursor:not-allowed}
.ogrp .gctl{display:inline-flex;gap:10px;align-items:center;margin-left:auto;font-size:11px;font-weight:400}
.ogrp .gctl select,.ogrp .gctl input{font-size:11px;padding:2px 4px}
.ogrp .gctl input.gdmult{width:64px}
input.ordmult{width:80px}
/* ONE ROW, ONE LINE. Fixed column widths, because the defect was a table that
   wrapped its widest cell and then clipped it anyway ("Line Ma…"). Item is the
   only elastic column and it ellipsises; SKU, Qty and Price never shrink. */
.ordtbl{width:100%;table-layout:fixed}
.ordtbl th,.ordtbl td{padding:6px 4px 6px 0;vertical-align:middle;
  white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.ordtbl th{font-size:9px;letter-spacing:.07em;text-transform:uppercase;
  color:var(--slate);border-bottom:1px solid var(--line)}
.ordtbl tr.opg td{padding:8px 0 3px;font-size:9px;letter-spacing:.07em;text-transform:uppercase;color:var(--slate);font-weight:700}
.ordtbl tr.opg label{cursor:pointer}
.ordtbl .opgtick{width:auto;height:auto;padding:0;margin:0 6px 0 0;flex:none;display:inline-block;vertical-align:-1px}
.ordtbl tr.off td{opacity:.4;text-decoration:line-through}
.ordtbl tr.opg.off td{text-decoration:none}
.ordtbl.orddealer td.n,.ordtbl.orddealer th.n{white-space:nowrap}
.ordtbl.orddealer td.osku{width:18%}
.ordswitch{display:flex;gap:0;margin:0 0 10px}
.ordswitch .ordview{flex:1;padding:6px 10px;border:1px solid var(--line);background:transparent;
  color:var(--slate);font-weight:600;cursor:pointer}
.ordswitch .ordview:first-child{border-radius:6px 0 0 6px}
.ordswitch .ordview:last-child{border-radius:0 6px 6px 0;border-left:0}
.ordswitch .ordview.on{background:var(--ink);color:#fff}
.ordtbl td.osku{width:25%;font-family:ui-monospace,Menlo,monospace;font-size:11px;
  font-weight:600}
.ordtbl td.oitem{width:auto;color:var(--slate);text-transform:capitalize}
/* Qty is 66px, not 34: the countertop's reads "10.76 sf" and Each "$65.00 / sf",
   and both were clipped to "..." on screen and in the PDF (s5 verify walk 1009). */
.ordtbl th.n,.ordtbl td.oqty{width:66px;text-align:right}
.ordtbl th:last-child,.ordtbl td.oprice{width:74px;text-align:right;
  font-family:var(--mono);font-variant-numeric:tabular-nums;padding-right:0}
/* Each (unit price), beside Price and set like it. */
.ordtbl th.oeach,.ordtbl td.oeach{width:88px;text-align:right;
  font-family:var(--mono);font-variant-numeric:tabular-nums}
.ordtbl th.oeach{font-family:inherit}
/* The order's date, at the right of its head like the plan print's. */
.orddate{margin-left:auto;align-self:flex-start;font-size:11.5px;color:var(--slate);white-space:nowrap;padding-top:3px}
.ordmore{display:block;width:100%;margin-top:8px;padding:7px 8px;
  background:transparent;color:var(--slate);border:1px dashed var(--line);
  border-radius:5px;font-size:11.5px;font-weight:600;cursor:pointer;
  font-family:var(--body)}
.ordmore:hover{background:var(--soft);color:var(--ink);border-color:var(--slate)}
/* DOWNLOAD PDF is the browser's print dialog with Save as PDF as the
   destination, so printing has to produce the ORDER and not the drawing board.

   EVERY RULE IS SCOPED TO body.printing-order, which only the Download PDF
   button sets and which is dropped again on afterprint. An unscoped print
   block would also capture File > Print and Ctrl+P — and those print the PLAN.
   It would have hidden .mid, which IS the canvas, so the one existing print
   path in this app would have produced a blank page. The renderer suite's
   "there is no separate print renderer" check is what caught it. */
@media print{
  /* #roombar and #sideL (s11 walk-through, 2026-10-03): the toolbar row left
     <header> at ui-0.60.0, after this block was written, and the side panel's
     BODY was never hidden — only its tabs — so the order PDF opened with the
     app's toolbar and whichever panel section was open, above the order. */
  body.printing-order header,body.printing-order #rail,body.printing-order .mid,
  body.printing-order #roombar,body.printing-order #sideL,
  body.printing-order #ptabs,
  body.printing-order .issuestrip,body.printing-order .ordacts,
  body.printing-order .ordbtns,body.printing-order .objpanel,
  body.printing-order .roommenu,body.printing-order .matpop,
  body.printing-order .ordmore,
  /* buttons that act on the screen: nothing to press on paper */
  body.printing-order #ordIssAll,body.printing-order .ordsugadd,
  /* The toolbar row and the right-hand panel moved out from under header and
     .mid, and printed above the order (s10 walk-through, 2026-10-03); the two
     overlays are top-level too. test_order.py walks every top-level part. */
  body.printing-order #roombar,body.printing-order #sideL,
  body.printing-order #specOverlay,body.printing-order #fbPanel,
  /* the rail's Counters and Floor boxes are top-level and fixed (batch 180) */
  body.printing-order .railbox,
  /* The toast is made by toast() at run time, so the walk over index.html
     never saw it: "Saved ..." from a Ctrl+S just before Download PDF printed
     at the foot of the quote (walk-through 1003235237). */
  body.printing-order #toast,body.printing-order .drawbar,
  /* ...and the layout question ("You already have a design open"), made by
     askShapeIntent at run time: left unanswered it printed in the quote's head */
  body.printing-order .askpop {display:none !important}   /* a space before the brace: tests/test_renderer_js finds the real .askpop rule by its brace */
  body.printing-order,body.printing-order main{display:block !important;
    height:auto !important;overflow:visible !important}
  body.printing-order #po{position:static !important;
    inset:auto !important;box-shadow:none !important;border:0 !important;
    padding:0 !important;overflow:visible !important;display:block !important}
  body.printing-order .ordbody{overflow:visible !important;max-height:none !important}
  body.printing-order .ordfoot{border-top:1px solid #000}
  /* A group heading (and the cost line under it) stays with its first line:
     the Dealer copy on Letter ended page 1 on "Mantra Omni · Hammock" and put
     its lines on page 2 (big kitchen walk, job 1010-0333). */
  body.printing-order .ogrp,body.printing-order .ocost{
    break-after:avoid;page-break-after:avoid}
  /* THE CUSTOMER COPY: #ordCopy (made by the Customer copy button) replaces
     the on-screen order on paper; the Dealer copy never makes it. */
  body.printing-order.printing-customer #po{display:none !important}
  body.printing-order.printing-customer #ordCopy{display:block}
}
#ordCopy{display:none}

/* HIDE / SHOW THE PANEL (ui-1.32.0, Nick: the bare ">" "looks like a
   tab-scroll arrow", and the collapsed strip is "nearly invisible").

   OPEN: a real button at the top-right of the header — 34px tall, its own
   border and fill, a hover state, and a label beside the icon. It is pushed
   away from the tabs with margin-left:auto so it cannot be read as a fourth
   tab. ORIGINAL KSP COLOURS: the neutral chrome greys this file already uses,
   never the rejected Crisp blue.

   COLLAPSED: the whole right edge becomes the control — 30px wide, full
   height, maroon --ksp-selection with a white icon and a vertical "Panel",
   which is the same token the active tab and the selected item use. A handle
   you can hit rather than a hairline you have to find. */
#ptabs .ptoggle{flex:0 0 34px;min-width:34px;min-height:32px;align-self:center;
  margin-left:auto;display:inline-flex;align-items:center;justify-content:center;
  gap:5px;border:1px solid #c3cbd6;background:#fff;color:#3b4553;
  border-radius:7px;cursor:pointer;font-size:14px;padding:0 8px;
  font-family:var(--body);white-space:nowrap}
#ptabs .ptoggle .ptlab{display:none}
#ptabs .ptoggle:hover{background:#e7ecf3;border-color:#93a0b2;color:#1b2430}
#ptabs .ptoggle:focus-visible{outline:2px solid var(--ksp-selection);outline-offset:1px}
/* Room for the words when the panel is wide enough to show them. */
@media (min-width:1200px){
  #ptabs .ptoggle{flex:0 0 auto;min-width:104px}
  #ptabs .ptoggle .ptlab{display:inline}
}
/* The panel's 1px left border is what pushed the handle off the right
   edge: 30px column + 1px border = 31. The maroon block IS the edge
   when collapsed, so the border has nothing left to do. */
main.wide #sideL{border-left:0}
main.wide #ptabs .ptoggle{flex:0 0 30px;min-width:30px;width:30px;align-self:stretch;
  margin-left:0;border-radius:0;border:0;padding:12px 0;gap:8px;
  background:var(--ksp-selection);color:#fff;
  writing-mode:vertical-rl;font-size:12px;letter-spacing:.08em;
  display:flex;align-items:center;justify-content:center}
main.wide #ptabs .ptoggle .ptlab{display:inline;text-transform:uppercase}
main.wide #ptabs .ptoggle:hover{background:#7d2b3e;color:#fff;border:0}
/* HANDING ON AN ORDER LINE (ui-1.36.0, Nick). Not an .oflag chip: those are
   coloured badges, and this is a sentence the shop reads off the printed
   sheet. It sits under the SKU on its own line and is deliberately NOT in the
   print stylesheet's hide list — Download PDF is the browser printing this
   table, so anything hidden there is missing from the quote. */
.ohand{display:block;margin-top:2px;font-size:11px;color:#5b4a2e;
  font-family:var(--label-sans);font-style:italic}
@media print{ .ohand{color:#000;font-style:normal} }
/* A changed cabinet size and its warning (job 12): beside the SKU, kept in print. */
.osize{display:block;margin-top:2px;font-size:11px;color:#8a2d1c;
  font-family:var(--label-sans);font-weight:600}
@media print{ .osize{color:#000} }
.oflag.auto{background:#e3e8ef;color:#394453}
.oflag.noprice{background:#b3261e;color:#fff}
.oflag.dead{background:#3d3d3d;color:#fff}
.ordsug{margin-top:10px}
.ordsug table{width:100%}
.autowhy{color:#5a6473}
.issueItem .issueSev{display:inline-block;margin-right:6px;padding:0 5px;border-radius:3px;font-size:10px;font-weight:700;text-transform:uppercase;letter-spacing:.04em;vertical-align:1px}
.issueItem.blocking .issueSev{background:#c98a0b;color:#fff}
.issueItem.review .issueSev{background:#e8dfc9;color:#6b5418}
.issuesHead .caret{color:#5a6473;transition:transform .12s}
.issuesSec.closed .caret{transform:rotate(-90deg)}
.issuesSec.closed .issuesBody{display:none}
.issuesBody{padding:2px 14px 14px}
.issueItem{background:#fdf1ea;border-left:3px solid var(--warn);border-radius:0 6px 6px 0;padding:8px 10px;margin-bottom:6px;font-size:12px;color:#7a3f22;line-height:1.4}
.issueOk{padding:8px 2px;font-size:12px;color:var(--ok)}
/* save dropdown */
.saveMenu{position:fixed;z-index:9999;background:#fff;border:1px solid #cfd6e0;border-radius:8px;box-shadow:0 6px 20px rgba(0,0,0,.18);padding:5px;min-width:150px;display:flex;flex-direction:column;gap:2px}
.saveMenu button{text-align:left;background:transparent;border:0;padding:8px 10px;border-radius:6px;font-size:13px;cursor:pointer;color:#1b2430}
.saveMenu button:hover{background:#eef1f5}
.saveMenu .amenu{display:flex;align-items:center;gap:8px;padding:8px 10px;font-size:13px;color:#1b2430;border-top:1px solid #eef1f5;margin-top:2px}
.saveMenuSep{height:1px;background:#eef1f5;margin:3px 4px}
.hfileacts{display:flex;gap:6px;margin-left:14px;align-items:center}
.hfileacts button{background:transparent;border:1px solid rgba(255,255,255,.25);color:#dfe6ee;border-radius:6px;padding:4px 10px;font-size:12px;cursor:pointer;font-family:var(--body)}
.hfileacts button:hover{background:rgba(255,255,255,.12);color:#fff}
.acc.closed .accbody{display:none}
.compact{margin:10px 0;padding:10px 0;border-top:1px solid var(--soft);border-bottom:1px solid var(--soft)}
.crow{display:flex;align-items:center;flex-wrap:wrap;gap:6px;margin-bottom:7px;min-height:24px}
.crow:last-child{margin-bottom:0}
/* WHY min-width AND NOT width. This was `width:46px;flex-shrink:0`, a HARD box.
   "DOOR", "FLOOR", "SPLASH" fit inside 46px; "COUNTERS" and "BACKSPLASH" do not,
   and a fixed-width flex item does not clip — the extra text paints outside the
   box, to the right. On the rows whose control is a <select>, the select is wide
   enough to wrap to its own line, so the overflow had empty space to sit in and
   looked fine. On the two rows whose control is a <button> (#countersRoom,
   #splashRoom), the button shrinks and stays on line 1, and being later in the
   DOM it paints ON TOP of the overflowing label — which is why exactly those two
   labels appeared cut off mid-word. min-width keeps the columns lined up for the
   short labels; width:auto lets the long ones claim the space they need. */
.clab{font-size:9px;font-weight:600;letter-spacing:.06em;text-transform:uppercase;
  color:var(--slate);min-width:46px;width:auto;white-space:nowrap;flex:0 0 auto}
.cmini{padding:3px 6px;font-size:11px;height:26px;min-width:0}
.cinline{display:inline-flex;align-items:center;gap:3px;font-size:10.5px;color:var(--slate);
  white-space:nowrap}
.cinline input{width:auto;margin:0;flex-shrink:0}
.compact .sw{display:flex;gap:3px;flex-wrap:wrap}
.compact .sw button{width:20px;height:20px}
::-webkit-scrollbar{width:8px}
::-webkit-scrollbar-thumb{background:#b7c0cd;border-radius:4px}
::-webkit-scrollbar-thumb:hover{background:#9aa6b6}
::-webkit-scrollbar-track{background:#eef1f5}
@media(prefers-reduced-motion:reduce){*{transition:none!important}}
.pickrow{display:flex;gap:10px;margin-bottom:12px}
.pickcol{flex:1;min-width:0}
/* Combined level toggle + wall stepper (Concept 1) */
.pickrow2{display:flex;align-items:center;gap:10px;flex-wrap:wrap;margin-bottom:12px}
.lvlseg{display:inline-flex;border:1px solid var(--line);border-radius:9px;overflow:hidden;background:#fff}
.lvlbtn{border:0;background:#fff;padding:7px 11px;cursor:pointer;color:var(--slate);
  font-size:12.5px;font-weight:600;display:inline-flex;align-items:center;gap:6px;line-height:1}
.lvlbtn+.lvlbtn{border-left:1px solid var(--line)}
.lvlbtn .ldot{width:8px;height:8px;border-radius:50%;background:#bbb}
.lvlbtn .ldot.base{background:#1e5028}
.lvlbtn .ldot.wall{background:#2850a0}
.lvlbtn .ldot.island{background:var(--clay)}
.lvlbtn .lct{font-family:var(--mono);font-size:10px;opacity:.7}
.lvlbtn.on{color:#fff}
.lvlbtn.on[data-t="base"]{background:#1e5028}
.lvlbtn.on[data-t="wall"]{background:#2850a0}
.lvlbtn.on[data-t="island"]{background:var(--clay)}
.lvlbtn.on .ldot{background:rgba(255,255,255,.9)}
.lvlbtn.on .lct{opacity:.85}
.wallstep{display:inline-flex;align-items:center;border:1px solid var(--line);
  border-radius:9px;overflow:hidden;background:#fff}
.wallstep.muted{opacity:.6}
.wsbtn{border:0;background:#fff;width:30px;height:34px;font-size:12px;cursor:pointer;color:var(--slate)}
.wsbtn:hover{background:#efe9df;color:var(--clay)}
.wsname{padding:0 12px;font-weight:700;font-size:12.5px;min-width:90px;text-align:center;
  display:inline-flex;align-items:center;justify-content:center;gap:6px}
.wsname.island{color:var(--slate);font-weight:600;font-size:11.5px}
.wsname .lct{font-family:var(--mono);font-size:10px;opacity:.6}
.totcab{margin-left:auto;font-family:var(--mono);font-size:11px;color:var(--slate);
  background:#fff;border:1px solid var(--line);border-radius:8px;padding:6px 10px;white-space:nowrap}
.totcab b{color:var(--ink);font-size:13px}
.pickcol .f{display:block;margin-bottom:5px;font-size:10px;font-weight:700;
  text-transform:uppercase;letter-spacing:.05em;color:var(--slate)}
.pickcol .hint2{font-weight:400;font-size:9px;color:var(--slate);text-transform:none;letter-spacing:0}
.cmini.wide{width:100%;box-sizing:border-box;padding:8px 30px 8px 11px;font-size:13px;
  font-weight:600;color:#2a2622;
  border:1px solid var(--line);border-radius:8px;background:#fbfaf7;cursor:pointer;
  height:auto;line-height:1.2;
  -webkit-appearance:none;-moz-appearance:none;appearance:none;
  background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 12 12'%3E%3Cpath d='M2 4l4 4 4-4' stroke='%237a746a' stroke-width='1.6' fill='none' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
  background-repeat:no-repeat;background-position:right 10px center;
  transition:border-color .12s, box-shadow .12s}
.cmini.wide:hover{border-color:#c2b8a6;background:#fff}
.cmini.wide:focus{outline:none;border-color:#7B1420;
  box-shadow:0 0 0 3px rgba(123,20,32,.10)}
/* ---- catalog picker (Vendor > Line > Finish) -------------------------------
   Sits ABOVE the cabinet tree so the panel reads top-to-bottom the way the job
   does: "the catalog I'm buying from" then "its cabinets". */
.catbox{border:1px solid var(--line);border-radius:6px;background:#fff;
  margin-bottom:8px;padding:8px 9px}
.catselrow{display:block;margin-bottom:9px}
.catsw{width:18px;height:18px;border-radius:4px;border:1px solid rgba(0,0,0,.25);
  flex:0 0 auto;margin-left:auto;box-shadow:inset 0 0 0 1px rgba(255,255,255,.35)}
.catsel{display:block;width:100%;min-width:0;box-sizing:border-box;
  font-size:12px;padding:7px 8px;border:1px solid var(--line);
  border-radius:5px;background:#fff;color:var(--ink);cursor:pointer}
.catsel:focus{outline:none;border-color:var(--clay,#7B1420);
  box-shadow:0 0 0 3px rgba(123,20,32,.10)}
.catsel:disabled{color:var(--slate);background:#f5f2ec;cursor:default}
.catcap{font-size:10px;color:var(--slate);margin:4px 0 0}
.catfavrow{margin:7px 0 0}
.catfavrow .s{font-size:11px;padding:3px 9px}
.catnote{font-size:10px;color:var(--slate);line-height:1.35;margin-top:7px;
  padding-top:7px;border-top:1px solid #f2efe9}
/* ---- recolour popup ---- */
.rcmask{position:fixed;inset:0;background:rgba(20,18,16,.42);z-index:900;
  display:flex;align-items:center;justify-content:center;padding:16px}
.rcbox{background:#fff;border-radius:10px;box-shadow:0 18px 50px rgba(0,0,0,.3);
  width:min(430px,100%);max-height:82vh;display:flex;flex-direction:column;overflow:hidden}
.rctop{padding:12px 14px 10px;border-bottom:1px solid var(--line)}
.rctop h3{margin:0 0 9px;font-size:14px;color:var(--ink)}
.rcto{display:flex;align-items:center;gap:8px;font-size:12px}
.rcto select{flex:1;padding:6px 8px;border:1px solid var(--line);border-radius:5px;
  font-size:12px;background:#fff}
.rcmid{padding:8px 14px;overflow-y:auto;flex:1}
.rcmid .lab{font-size:11px;color:var(--slate);margin:2px 0 6px}
.rcgrp{display:flex;align-items:center;gap:8px;padding:6px 4px;cursor:pointer;
  border-radius:5px;font-size:12px}
.rcgrp:hover{background:var(--paper)}
.rcgrp.sel{background:var(--sage-tint,#eef1ec)}
.rcgrp input{margin:0;flex:0 0 auto;width:auto;min-width:0;padding:0}
.rcgrp .sw{width:16px;height:16px;border-radius:4px;border:1px solid rgba(0,0,0,.25);flex:0 0 auto}
.rcgrp .nm{flex:1;min-width:0;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.rcgrp .qt{font-size:11px;color:var(--slate);font-variant-numeric:tabular-nums}
.rcbot{padding:10px 14px;border-top:1px solid var(--line);display:flex;
  justify-content:flex-end;gap:8px;background:#faf8f4}
.rcwarn{font-size:10.5px;color:var(--warn,#9a5b00);padding:4px 4px 0;line-height:1.35}
/* ---- colour scheme control ---- */
.schrow{display:flex;align-items:center;gap:6px;margin:0 0 6px}
.schrow .lb{font-size:11px;color:var(--slate);flex:0 0 auto}
.schseg{display:flex;border:1px solid var(--line);border-radius:5px;overflow:hidden}
.schseg button{border:0;background:#fff;padding:4px 10px;font-size:11px;cursor:pointer;
  color:var(--slate);border-right:1px solid var(--line)}
.schseg button:last-child{border-right:0}
.schseg button.on{background:var(--clay,#7B1420);color:#fff;font-weight:600}
.schpick{display:flex;align-items:center;gap:6px;margin:0 0 5px;font-size:11px}
.schpick .k{flex:0 0 68px;color:var(--slate)}
.schpick select{flex:1;padding:4px 6px;border:1px solid var(--line);border-radius:5px;font-size:11px}
/* ---- order: variant group headings + price-review flag ---- */
.ogrp{margin:10px 0 2px;padding:5px 7px;background:var(--sage-tint,#eef1ec);
  border-left:3px solid var(--clay,#7B1420);border-radius:0 4px 4px 0;
  display:flex;align-items:center;gap:7px}
.ogrp .sw{width:14px;height:14px;border-radius:3px;border:1px solid rgba(0,0,0,.25);flex:0 0 auto}
.ogrp .nm{flex:1;font-size:11.5px;font-weight:700;color:var(--ink)}
.ogrp .qt{font-size:10.5px;color:var(--slate)}
.ogrp .st{font-size:11.5px;font-weight:700;font-variant-numeric:tabular-nums}
/* ---- section 5: the reconciliation panel ---- */
/* A panel, not a modal over the drawing: the dealer needs to look at the
   design while answering, and each row is answered on its own so there is no
   moment where the whole thing must be dealt with before work resumes. */
.recpop{position:fixed;right:18px;bottom:18px;z-index:68;width:340px;
  max-height:min(70vh,620px);overflow-y:auto;
  background:var(--paper,#fff);border:1px solid var(--line);
  border-radius:8px;box-shadow:0 8px 28px rgba(0,0,0,.22);
  font-size:12px;color:var(--ink)}
.rechd{position:sticky;top:0;display:flex;align-items:center;gap:8px;
  padding:9px 11px;background:var(--sage-tint,#eef1ec);
  border-bottom:1px solid var(--line);border-radius:8px 8px 0 0}
.rechd b{flex:1;font-family:var(--display);font-size:12px}
.recx{border:0;background:none;font-size:17px;line-height:1;cursor:pointer;
  color:var(--slate);padding:0 2px}
.recnote{margin:0;padding:9px 11px;color:var(--slate);font-size:11px;
  border-bottom:1px solid var(--soft)}
.recrow{padding:9px 11px;border-bottom:1px solid var(--soft)}
.recrow.done{opacity:.62}
.recwhat{font-weight:700;font-size:11px}
.recwho{font-family:var(--mono);font-size:11.5px;margin-top:1px}
.recdet{color:var(--slate);font-size:11px;margin-top:1px;
  font-variant-numeric:tabular-nums}
.recagain{margin-top:4px;padding:4px 6px;border-radius:4px;
  background:#fdf1dc;color:#9a5b00;font-size:10.5px}
/* Both choices weighted the same. One styled as primary would be a
   recommendation, and section 5's whole point is that the system does not
   have one. Only "remove" is marked, because it destroys work. */
.recacts{display:flex;gap:6px;margin-top:7px;flex-wrap:wrap}
.recbtn{flex:1;min-width:120px;padding:5px 7px;font-size:11px;cursor:pointer;
  border:1px solid var(--line);border-radius:5px;background:#fff;
  color:var(--ink)}
.recbtn:hover{background:var(--soft)}
.recbtn.warn{border-color:#c9a2a2;color:#8d2020}
.recbtn:disabled{opacity:.5;cursor:default}
.recdone{color:var(--slate);font-size:10.5px;font-style:italic}
.note.dead{color:#8d2020;background:#fbeeee;border-radius:5px;padding:6px 8px}
.note.dead .lnk{border:0;background:none;padding:0;margin-left:4px;
  color:#8d2020;text-decoration:underline;cursor:pointer;font-size:inherit}

/* ---- order: section 4's cost line and sale block ---- */
/* Deliberately quieter than .ogrp — the cost line sits UNDER a group heading
   and is a consequence of it, not a second heading. It is also not editable:
   nothing in here is an input, because a dealer's cost is set above them. */
.ocost{margin:0 0 2px;padding:3px 7px 3px 10px;
  border-left:3px solid var(--sage,#9fb0a0);
  display:flex;align-items:center;gap:7px;
  font-size:10.5px;color:var(--slate)}
.ocost .nm{flex:1}
.ocost .st{font-weight:700;font-variant-numeric:tabular-nums}
.custctl{margin:0 0 10px}
.ordcustsec{display:block;margin:0 0 10px;font-size:12px}
.ordcustsec input{font-size:12px;padding:3px 5px;border:1px solid var(--line);border-radius:6px;background:#fff}
.ordcustform label{display:block;margin:3px 0;font-size:11px;color:var(--slate)}
.ordcustform label input{display:block;width:100%;box-sizing:border-box}
.ordcustfind{display:flex;gap:6px;margin-top:4px}.ordcustfind input{flex:1;min-width:0}
.ordcustres{display:flex;flex-direction:column;align-items:flex-start;margin-top:3px}
.ordcust{font-size:11.5px;color:var(--slate)}
.ordcustname{font-weight:700;color:#1b2430;font-size:13px}
.ordcust.none{font-style:italic}
.ordsum{border:1px solid #9aa4b2;border-radius:6px;padding:6px 10px;margin:8px 0 10px;font-size:12px;break-inside:avoid}
.ordsumttl{font-weight:700;font-size:12px;text-transform:uppercase;letter-spacing:.04em;color:var(--slate);margin-bottom:3px}
.ordsumrow{display:flex;flex-wrap:wrap;gap:2px 8px;align-items:baseline}
.ordsumrow .k{color:var(--slate)}
.ordsumedit{flex-wrap:wrap;margin:0 0 10px;font-size:12px}
.ordsumedit input,.ordsumedit select{font-size:12px;padding:3px 5px;border:1px solid var(--line);border-radius:6px;background:#fff}
.custctl select{flex:1;min-width:0}
.ordsale{margin:0 0 13px;padding:7px 9px;border:1px solid var(--line);
  border-radius:6px;background:var(--sage-tint,#eef1ec)}
.ordsale .salerow{display:flex;align-items:baseline;justify-content:space-between;
  gap:8px;font-size:11.5px;color:var(--ink)}
.ordsale .salerow b{font-variant-numeric:tabular-nums}
.ordsale .salerow.marg{margin-top:3px;padding-top:3px;
  border-top:1px dashed var(--line);font-weight:700}
.ordsale .salerow.dsc{color:var(--slate);font-size:11px}
.oflag{display:inline-block;margin-left:5px;padding:0 4px;border-radius:3px;
  background:#fdf1dc;color:#9a5b00;font-size:9.5px;font-weight:700;
  vertical-align:middle;cursor:help}
.osku{font-family:ui-monospace,Menlo,monospace;font-size:11px}
.cabtree{border:1px solid var(--line);border-radius:6px;overflow:hidden;margin-bottom:8px}
.cabsearch{width:100%;box-sizing:border-box;border:0;border-bottom:1px solid var(--line);
  padding:8px 10px;font-size:12px;outline:none}
.cabtreebody{max-height:260px;overflow-y:auto}
.tnode{border-bottom:1px solid var(--line)}
.thead{padding:7px 10px;font-size:12px;font-weight:600;cursor:pointer;
  background:var(--card);color:var(--ink);user-select:none}
.thead:hover{background:var(--paper)}
.tcaret,.wcaret,.hcaret{display:inline-block;width:12px;color:var(--slate);font-size:10px}
.tkids{background:#fff}
.hhead{padding:5px 10px 5px 22px;font-size:11px;font-weight:600;cursor:pointer;
  color:var(--ink);user-select:none;background:#faf8f4;border-top:1px solid #f2efe9}
.hhead:hover{color:var(--clay)}
.hkids{background:#fff}
.whead{padding:5px 10px 5px 34px;font-size:11px;cursor:pointer;color:var(--slate);user-select:none}
.whead:hover{color:var(--clay)}
.skus{}
.skurow{padding:5px 10px 5px 46px;font-size:11px;font-family:var(--mono);
  cursor:grab;color:var(--ink);border-top:1px solid #f2efe9}
.skurow:hover{background:var(--clay);color:#fff}
.skurow:active{cursor:grabbing}
.treenone{padding:14px;text-align:center;color:var(--slate);font-size:12px}

/* --- the cabinet picker's CHIPS (ui-0.86.0) -----------------------------
   Deliberately NOT .wpb, the wall panel's button: that one is flex:1, so it
   stretches to share a row, and it carries the wall panel's blue. A facet chip
   is the width of its own label and reads as a filter, not as an action. */
.cchips{display:flex;flex-wrap:wrap;gap:4px;padding:6px 8px;
  border-bottom:1px solid #f2efe9;background:#faf8f4}
.cchips:last-of-type{border-bottom:1px solid var(--line)}
.cchip{flex:0 0 auto;padding:3px 9px;font-size:11px;line-height:1.5;cursor:pointer;
  border:1px solid var(--line);border-radius:11px;background:#fff;color:var(--ink);
  white-space:nowrap;user-select:none}
.cchip:hover{border-color:var(--clay);color:var(--clay)}
/* ON is the same maroon the rest of the app selects with (--ksp-selection),
   so a picked chip reads as the same decision as a selected row or tab. */
.cchip.on{background:var(--ksp-selection);border-color:var(--ksp-selection);
  color:#fff;font-weight:600}
.cchip.on:hover{color:#fff}
.cchip .n{font-family:var(--mono);font-size:9.5px;color:var(--slate);
  margin-left:3px;vertical-align:baseline}
.cchip.on .n{color:#fff;opacity:.8}
/* DIM = the current search empties this chip. Faded, never hidden and never
   disabled: the count is the whole catalog's, so a dim chip is how you see
   that what you typed is somewhere else. */
.cchip.dim{opacity:.4}
/* Heights and widths are numbers and line up as numbers. */
.cwchip{font-family:var(--mono);font-size:10.5px}
/* The list under the chips is flat, so it loses the tree's three indents. */
.cablist .skurow{padding-left:10px}
.roombar{position:relative;display:flex;align-items:center;gap:8px;padding:6px 14px;
  background:#eef1f5;border-bottom:1px solid #cfd6e0;flex-wrap:wrap;
  /* Reserve the settled height up front. The toolbar (.rbtools) wraps across
     rows and its height only settles a beat after load as buttons/fonts size up;
     without a reserved min-height the bar grows and shoves #main down — that was
     the single 0.28 CLS shift. box-sizing so padding is included. */
  box-sizing:border-box;min-height:42px;align-content:center}
.rblabel{font-size:10px;text-transform:uppercase;letter-spacing:.06em;color:var(--slate)}
.roomtabs{display:flex;gap:4px;flex-wrap:wrap}
/* `hidden` must beat the display rules above — the rooms controls moved into the
   Room tab dropdown in the header, so these legacy nodes stay in the DOM (their
   handlers are reused) but must never render. */
[hidden]{display:none !important}
.roomtabs button{font-size:12px;padding:4px 12px;border:1px solid var(--line);
  background:transparent;border-radius:5px;cursor:pointer;color:var(--slate)}
.roomtabs button.on{background:var(--clay);color:#fff;border-color:var(--clay)}
.rbadd{font-size:12px;padding:4px 10px;border:1px solid var(--clay);color:var(--clay);
  background:transparent;border-radius:5px;cursor:pointer}
.rbadd:hover{background:var(--clay);color:#fff}
.rbmini{font-size:11px;padding:4px 8px;border:1px solid var(--line);color:var(--slate);
  background:transparent;border-radius:5px;cursor:pointer}
.rbmini:hover{border-color:var(--slate)}
/* RESERVE THE BAND. Without this the toolbar's own buttons wrap UNDERNEATH
   the pinned strip rather than stopping short of it — and it would look fine
   until the window got narrow, which is the same class of bug as the strip
   dropping to a second row. */
.rbtools{flex:1;min-width:0;display:flex;align-items:center;gap:5px;flex-wrap:wrap;
  justify-content:flex-start;padding-right:var(--panelw);box-sizing:border-box}
.rbtools .s,#gridzoom .s{font-size:11px;padding:3px 8px}
.rbtools .seg2 button{font-size:11px;padding:3px 8px}
.rbtools .vtools{display:flex;gap:5px;flex-wrap:wrap}
/* THE CANVAS TOOLBAR IS NOT A FORM. The panel's field rule near the top of this
   file — `select,input{width:100%;max-width:100%}` — is written for a stacked
   side-panel field and leaks into every control in the bar, where a control sits
   in a flex row and must be its own width. #elevWallSel, whose longest option is
   "Back wall", rendered 349px wide and took the Elevation toolbar to three rows
   on a 1440 screen; #crownsel and #magnetRange already carried inline widths to
   work around the same rule, which is the tell. Scope it out here rather than
   adding a third inline workaround. */
.rbtools select,.rbtools input{width:auto;max-width:170px;min-width:0}

/* The ⚙ Render launcher, now sitting in the view-tab row beside .seg2.
   It is an ACTION, not a fourth view, so it deliberately does NOT inherit the
   segmented control's look — its own border, its own fill, and it never takes
   the `.on` selected state the view tabs use. */
.rbaction{ display:inline-flex; align-items:center; gap:4px;
           font-size:11px; font-weight:600; padding:4px 10px;
           border:1px solid var(--ink); border-radius:3px;
           background:var(--ink); color:var(--paper);
           cursor:pointer; white-space:nowrap; }
.rbaction:hover{ filter:brightness(1.18); }
.rbaction:active{ filter:brightness(.92); }
/* ---- appliance browser ------------------------------------------------- */
#stage{position:relative}
#appview{position:absolute;inset:0;overflow:hidden;background:#fff;z-index:40}
.appwrap{display:flex;height:100%}
.applist{width:290px;flex:none;border-right:1px solid #e3e6ea;overflow-y:auto;
  padding:14px 0 22px;background:#fafbfc}
.appeyebrow{font:600 10px/1 Roboto Mono,ui-monospace,monospace;letter-spacing:.14em;
  text-transform:uppercase;color:#8a939d;padding:0 16px;margin:14px 0 8px}
.appeyebrow:first-child{margin-top:0}
.appitem{display:block;width:100%;text-align:left;background:none;border:0;
  padding:9px 16px;cursor:pointer;border-left:3px solid transparent;font:inherit;color:#1e232a}
.appitem:hover{background:#eef1f4}
.appitem.on{background:#eef1f4;border-left-color:#b8933f}
.appmore{display:block;width:100%;margin:8px 0;padding:8px;background:#fff;border:1px solid #c9ced4;border-radius:4px;cursor:pointer;font:inherit}
.appitem .an{display:block;font:600 13px/1.3 inherit}
.appitem small{display:block;color:#79828c;
  font:11px/1.4 Roboto Mono,ui-monospace,monospace;margin-top:2px}
.appitem.on small{color:#96742c}
.appdetail{flex:1;min-width:0;overflow-y:auto;padding:30px 34px}
.appdetail h2{margin:0;font:700 25px/1.2 inherit;color:#171b21}
.appsku{margin:5px 0 22px;color:#79828c;
  font:12px/1 Roboto Mono,ui-monospace,monospace;letter-spacing:.05em}
.apptab{border-collapse:collapse;width:100%;max-width:560px}
.apptab th{text-align:left;vertical-align:top;padding:9px 18px 9px 0;width:150px;
  font:600 11px/1.5 Roboto Mono,ui-monospace,monospace;letter-spacing:.08em;
  text-transform:uppercase;color:#8a939d;border-bottom:1px solid #eceff2}
.apptab td{padding:9px 0;font:13px/1.5 inherit;color:#1e232a;
  border-bottom:1px solid #eceff2}
.apptab td.hot{color:#8a6a1e;font-weight:600}
.apptab td em{color:#8a939d;font-style:normal;font-size:11px}
.appadd{display:flex;gap:9px;margin-top:24px}
.appbtn{background:#1f5fbf;color:#fff;border:0;border-radius:5px;padding:9px 16px;
  font:600 13px inherit;cursor:pointer}
.appbtn:hover{background:#1a4f9e}
.appbtn.g{background:#e8ebef;color:#2b3138}
.appbtn.g:hover{background:#dde1e6}
.appbtn:disabled{opacity:.5;cursor:default}
.apphint{margin-top:22px;padding:12px 14px;background:#f6f7f9;border-radius:5px;
  color:#5d666f;font:12px/1.55 inherit;max-width:560px}
/* Above the cabinet menu (.cabpop.cabmenu, z 10000) that opens it: at 60 it
   opened UNDER the menu, and a click on its first parts hit Duplicate
   (walk-through 1003235237). */
.edgepop{position:fixed;z-index:10001;overflow-y:auto;overscroll-behavior:contain;background:#fff;border:1px solid #d8dde3;
  border-radius:7px;box-shadow:0 10px 34px rgba(20,26,34,.20);padding:9px 0;
  min-width:210px;max-height:60vh;overflow-y:auto}
.edgehd{font:600 11px/1 Roboto Mono,ui-monospace,monospace;letter-spacing:.09em;
  text-transform:uppercase;color:#8a939d;padding:2px 14px 9px;
  border-bottom:1px solid #eceff2;margin-bottom:6px}
.edgesec{font:600 10px/1 Roboto Mono,ui-monospace,monospace;letter-spacing:.1em;
  text-transform:uppercase;color:#9aa3ad;margin:9px 0 3px;padding:0 14px}
.edgeitem{display:flex;justify-content:space-between;align-items:baseline;gap:12px;
  width:100%;text-align:left;background:none;border:0;padding:6px 14px;
  cursor:pointer;font:inherit;color:#1e232a}
.edgeitem:hover{background:#eef1f4}
.edgeitem span{font:600 12px inherit}
.edgeitem small{color:#79828c;font:11px Roboto Mono,ui-monospace,monospace}
.edgebtn,.flipbtn{white-space:nowrap}
.ihead{font:600 10px/1 Roboto Mono,ui-monospace,monospace;letter-spacing:.11em;
  text-transform:uppercase;color:#8a939d;margin:14px 0 5px;padding:0 2px;
  border-bottom:1px solid #eceff2;padding-bottom:5px}
.ihead:first-child{margin-top:2px}
.askpop{position:fixed;z-index:70;overflow-y:auto;overscroll-behavior:contain;background:#fff;border:1px solid #d8dde3;
  border-radius:7px;box-shadow:0 10px 34px rgba(20,26,34,.20);padding:12px;width:230px}
.askq{margin:0 0 10px;font:13px/1.45 inherit;color:#1e232a}
.askbtn{display:block;width:100%;text-align:left;background:#f4f6f8;border:1px solid #dfe4ea;
  border-radius:5px;padding:8px 11px;margin-bottom:6px;font:13px inherit;color:#1e232a;cursor:pointer}
.askbtn:hover{background:#e9edf1}
.asklink{background:none;border:0;padding:4px 2px;font:12px inherit;color:#79828c;cursor:pointer}
.asklink:hover{color:#1e232a;text-decoration:underline}
.drawbar{position:fixed;left:50%;transform:translateX(-50%);bottom:22px;z-index:80;
  display:flex;align-items:center;gap:10px;background:#fff;border:1px solid #d8dde3;
  border-radius:999px;box-shadow:0 8px 26px rgba(20,26,34,.18);padding:7px 9px 7px 16px}
.drawbar span{font:12px Roboto Mono,ui-monospace,monospace;color:#79828c}
/* The gesture, said where the count is (ui-0.82.0): finishing is a CLICK
   now, not a remembered key, and a bar that only counts walls does not say
   so. Muted and smaller than the count, which is the thing being read. */
.drawbar .dhint{font-size:11px;color:#9aa3ad;opacity:.9}
.dbtn{background:#f4f6f8;border:1px solid #dfe4ea;border-radius:999px;padding:6px 13px;
  font:13px inherit;color:#1e232a;cursor:pointer}
.dbtn:hover{background:#e9edf1}
.dbtn.x{background:#1f5fbf;border-color:#1f5fbf;color:#fff}
.dbtn.x:hover{background:#1a4f9e}
.lgempty{margin:10px 2px 4px;font:12px inherit;color:#79828c}
/* The wall editor. `overflow-y:auto` + a maxHeight from _place() means this
   SCROLLS when the window is short — but a scroll container's bottom padding
   is not honoured at the end of the scroll in every engine, so the last line
   sat flush against the edge and read as cut off (Nick, 2026-09-16: "its
   bottom hint text is cut off"). The padding stays for the look; the last
   child carries a real margin, which every engine does honour. */
.wallpop{position:fixed;z-index:75;overflow-y:auto;overscroll-behavior:contain;background:#fff;border:1px solid #d8dde3;
  border-radius:8px;box-shadow:0 10px 34px rgba(20,26,34,.20);padding:12px 14px;width:212px}
.wallpop>*:last-child{margin-bottom:6px}
.wphd{display:flex;align-items:center;gap:8px;
  border-bottom:1px solid #eceff2;padding-bottom:8px;margin-bottom:10px}
.wphd b{font:600 14px inherit;color:#1e232a}
.wphd span{font:11px Roboto Mono,ui-monospace,monospace;color:#8a939d;
  margin-left:auto;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
/* Delete, top right (ui-0.80.0). Muted until hovered: it is the rarest thing
   in the panel and it used to be the loudest. The negative margin lets the
   hit area be a comfortable 24px without pushing the header taller. */
.wptrash{flex:none;background:none;border:0;border-radius:5px;
  padding:3px 4px;margin:-3px -5px -3px 0;line-height:1;font-size:15px;
  color:#9aa3ad;cursor:pointer}
.wptrash:hover{background:#fdf2f2;color:#c0392b}
.wpl{display:flex;align-items:center;gap:8px;margin-bottom:8px;
  font:12px inherit;color:#5d666f}
.wpl input,.wpl select{flex:1;min-width:0;border:1px solid #dfe4ea;border-radius:5px;
  padding:5px 7px;font:12px Roboto Mono,ui-monospace,monospace;color:#1e232a}
.wpl span{font:11px Roboto Mono,monospace;color:#9aa3ad}
.wprow{display:flex;gap:5px;margin:2px 0 4px}
.wpsec{font:600 10px Roboto Mono,monospace;letter-spacing:.1em;text-transform:uppercase;
  color:#9aa3ad;margin:12px 0 6px}
.wpb{background:#f4f6f8;border:1px solid #dfe4ea;border-radius:5px;padding:5px 9px;
  font:12px inherit;color:#1e232a;cursor:pointer;flex:1}
.wpb:hover{background:#e9edf1}
.wphint{margin:10px 0 0;font:11px inherit;color:#9aa3ad;line-height:1.4}

/* WALLS BY NUMBERS (ui-0.81.0) — the Wall lengths step's table.
   One row per wall: its number, its length, and the turn that gets to it from
   the wall before. Three columns and no more, because the row has to fit the
   212px panel on one line — the wall CHIPS it replaces were compact for the
   same reason, and a row that wraps is a table nobody can read down.
   No palette of its own: --line, --slate and --warn, the same tokens the rest
   of the panel uses. */
.wntab{width:100%;border-collapse:collapse;font-size:12px}
.wntab th{font:600 9px var(--body);letter-spacing:.06em;text-transform:uppercase;
  color:var(--slate);text-align:left;padding:0 3px 3px;font-weight:600}
.wntab th.n,.wntab td.n{width:20px;text-align:center}
.wntab td{padding:1px 3px;vertical-align:middle}
.wntab td.n{font:600 11px var(--mono);color:var(--slate);cursor:pointer;
  border-radius:3px}
.wntab td.n:hover{background:var(--soft)}
.wntab tr.on td.n{background:#e6d3c8;color:#7a3f1e}
.wntab input,.wntab select{padding:4px 5px;font-size:11.5px}
.wntab input.wnlen,.wntab input.wndeg{font-family:var(--mono)}
/* The first wall has nothing to turn FROM. An empty cell would read as a
   control that failed to draw, so it says so. */
.wntab td.wnfirst{font-size:10.5px;color:var(--slate);padding-left:6px}
.wnturnwrap{display:flex;gap:3px}
.wnturnwrap .wndeg{flex:0 0 46px}
/* The close control. The gap is ON the button, so the number is read before
   anything is clicked rather than in a dialog after. */
.wnclose{margin-top:9px}
.wnclose button{width:100%}
.wnnote{margin:4px 0 0;font-size:10.5px;color:var(--slate);line-height:1.4}
.wnwarn{margin:4px 0 0;font-size:10.5px;color:var(--warn);font-weight:600;
  line-height:1.4}
.objpanel{position:fixed;right:14px;top:96px;z-index:65;width:236px;max-height:70vh;
  overflow-y:auto;background:#fff;border:1px solid #d8dde3;border-radius:8px;
  box-shadow:0 10px 34px rgba(20,26,34,.18);padding:10px 0 12px}
.ophd{display:flex;justify-content:space-between;align-items:center;
  padding:0 12px 9px;border-bottom:1px solid #eceff2;
  font:600 12px inherit;color:#1e232a}
.opx{background:none;border:0;font:14px inherit;color:#8a939d;cursor:pointer;padding:0 2px}
.opx:hover{color:#1e232a}
.opsec{font:600 10px Roboto Mono,ui-monospace,monospace;letter-spacing:.11em;
  text-transform:uppercase;color:#9aa3ad;margin:11px 0 4px;padding:0 12px}
.oprow{display:block;width:100%;text-align:left;background:none;border:0;
  padding:6px 12px;cursor:pointer;font:inherit;color:#1e232a;border-left:3px solid transparent}
.oprow:hover{background:#eef1f4}
.oprow.on{background:#eef1f4;border-left-color:#b4643c}
.oprow span{display:block;font:600 12px inherit}
.oprow small{display:block;color:#79828c;font:11px Roboto Mono,ui-monospace,monospace;margin-top:1px}
.opempty{margin:2px 12px 4px;font:11px inherit;color:#9aa3ad}
.wpb.sel{background:#e6d3c8;border-color:#b4643c;color:#7a3f1e;font-weight:600}
.wpdel{display:block;width:100%;margin-top:12px;background:#fdf2f2;border:1px solid #f0c9c9;
  border-radius:5px;padding:7px;font:12px inherit;color:#a03434;cursor:pointer}
.wpdel:hover{background:#f9e3e3}
.dsgnpop{position:fixed;z-index:78;width:280px;max-height:66vh;overflow-y:auto;
  background:#fff;border:1px solid #d8dde3;border-radius:8px;
  box-shadow:0 10px 34px rgba(20,26,34,.20);padding:10px 0 12px}
.dsgnsave{display:flex;gap:6px;padding:10px 12px;border-bottom:1px solid #eceff2}
.dsgnsave input{flex:1;min-width:0;border:1px solid #dfe4ea;border-radius:5px;
  padding:6px 8px;font:12px inherit;color:#1e232a}
.dsgnrow{display:flex;align-items:stretch}
.dsgnopen{flex:1;text-align:left;background:none;border:0;padding:7px 12px;
  cursor:pointer;font:inherit;color:#1e232a;min-width:0}
.dsgnopen:hover{background:#eef1f4}
.dsgnopen span{display:block;font:600 12px inherit;white-space:nowrap;
  overflow:hidden;text-overflow:ellipsis}
.dsgnopen small{display:block;color:#79828c;font:11px Roboto Mono,ui-monospace,monospace}
.dsgndel{background:none;border:0;border-left:1px solid #dfe3e8;margin-left:14px;min-width:44px;color:#c0a3a3;cursor:pointer;padding:0 10px;font:13px inherit}
.dsgndel:hover{color:#a03434;background:#fdf2f2}
.dsgnrowgone{flex-wrap:wrap;border-bottom:1px solid #eceff2;padding-bottom:6px}
.dsgnrowgone .dsgnopen{flex:1 1 100%;cursor:default}
.dsgnrowgone .dsgnopen:hover{background:none}
.dsgnrowgone .dsgnopen span{white-space:normal}
.dsgnact{background:none;border:1px solid #dfe3e8;border-radius:5px;margin:0 0 0 12px;color:#2c3440;cursor:pointer;padding:4px 10px;font:11px inherit}
.dsgnact:hover{background:#eef1f4}
.dsgnver{background:none;border:0;border-left:1px solid #dfe3e8;margin-left:14px;color:#79828c;cursor:pointer;padding:0 10px;font:11px inherit}
.dsgnver:hover{color:#2c3440;background:#eef1f4}
.dsgnverhd{margin:6px 12px;font:12px inherit;color:#2c3440}
.dsgnverhd .dsgnact{margin:0 8px 0 0}
/* ============================================================= FEEDBACK ==
   Tester bug reports. Deliberately loud and always reachable: the whole point
   is that a tester files the report AT the moment the bug is on screen, while
   the scene and the movement trace still describe it. Anything that costs a
   navigation gets reported tomorrow, from memory, or not at all. */
/* Lives in the top menu bar after Print, so it inherits button.s wholesale —
   no position, colour or sizing of its own. Only the count badge needs rules,
   and it rides on the dark button ground the bar already uses. */
#fbBtn .fbdot,#fxBtn .fbdot{display:inline-block;min-width:15px;padding:0 4px;margin-left:6px;
  border-radius:8px;background:rgba(255,255,255,.24);font:600 10px var(--mono);
  line-height:15px;text-align:center;vertical-align:1px}
#fbBtn.g .fbdot,#fxBtn.g .fbdot{background:rgba(27,25,23,.14)}

/* BELOW THE CHROME, NOT OVER IT (ui-0.62.4). At top:0 the drawer slid out
   across the header — hiding Specs, Order and the account menu — and across
   the toolbar's right end, clipping the Log button. --chrome-h is published by
   syncChromeVars() in index.html and follows the header and toolbar as they
   wrap. The fallback matters: if the property is ever missing the drawer must
   still clear a normal header rather than land back on top of it. */
#fbPanel{position:fixed;top:var(--chrome-h,90px);right:0;bottom:0;width:400px;max-width:94vw;z-index:1400;
  background:var(--card);border-left:1px solid var(--line);display:flex;flex-direction:column;
  box-shadow:-14px 0 40px rgba(20,26,34,.22);
  /* visibility rides along on a delay so the closed panel cannot be tabbed
     into from off-screen — transform alone leaves it focusable. */
  transform:translateX(100%);visibility:hidden;
  transition:transform .18s ease-out,visibility 0s linear .18s}
#fbPanel.on{transform:translateX(0);visibility:visible;
  transition:transform .18s ease-out}
/* The panel is a form. Leaving it open while the tester goes back to the plan
   to check one more thing is normal, so it does not trap focus or dim the app. */
.fbhd{display:flex;align-items:center;gap:10px;padding:11px 14px;
  background:#1d1f22;color:var(--paper);flex-shrink:0}
.fbhd b{font:600 14px var(--display);letter-spacing:-.01em}
.fbhd .fbwho{margin-left:auto;font:11px var(--mono);color:#82868c}
.fbhd .fbx{background:none;border:0;color:#9aa1a8;font-size:16px;cursor:pointer;padding:0 2px}
.fbhd .fbx:hover{color:#fff}

.fbform{padding:12px 14px;border-bottom:1px solid var(--soft);flex-shrink:0;background:#fff}
.fbform input,.fbform textarea{width:100%;border:1px solid #dfe4ea;border-radius:5px;
  padding:7px 9px;font:13px var(--body);color:#1e232a;background:#fff}
.fbform textarea{margin-top:6px;resize:vertical;min-height:62px;line-height:1.45}
.fbform input:focus,.fbform textarea:focus{outline:0;border-color:#185FA5;
  box-shadow:0 0 0 2px rgba(24,95,165,.13)}
.fbattach{display:flex;align-items:center;gap:6px;flex-wrap:wrap;
  margin:8px 0 0;font:11px var(--mono);color:#79828c}
.fbattach i{font-style:normal;background:#eef1f4;border:1px solid #e2e7ec;
  border-radius:3px;padding:1px 6px;color:#4a545e}
.fbsend{width:100%;margin-top:9px;background:#7B1420;color:#fff;border:0;border-radius:5px;
  padding:9px;font:600 13px var(--body);cursor:pointer}
.fbsend:hover{background:#96202e}
.fbsend:disabled{background:#b9a0a4;cursor:default}

.fbtabs{display:flex;flex-shrink:0;background:#fff;border-bottom:1px solid var(--soft)}
.fbtabs button{flex:1;background:none;border:0;border-bottom:2px solid transparent;
  padding:9px 4px;font:600 12px var(--body);color:#79828c;cursor:pointer}
.fbtabs button:hover{background:#f4f6f8;color:#4a545e}
.fbtabs button.on{color:#1e232a;border-bottom-color:#7B1420}
.fbtabs button small{font:11px var(--mono);color:#9aa3ad;margin-left:4px}

.fblist{flex:1;overflow-y:auto;padding:8px 0 24px}
.fbempty{margin:16px 14px;font:12px var(--body);color:#9aa3ad;line-height:1.5}
.fbrow{border-bottom:2px solid #3a424b;border-left:6px solid transparent;background:#fff}
.fblist>.fbrow:first-of-type{border-top:2px solid #3a424b}
/* Sent to Claude: the whole row, not only its tag (Nick, 2026-10-07). */
.fbrow.sent{background:#fffbea}
.fbrow.sent>button.fbhead:hover,.fbrow.sent.open>button.fbhead{background:#fff3c4}
.fbrow.sent .fbstage.s2,.fbrow.sent .fbstage.s3{background:#fff}
.fbrow>button.fbhead{display:block;width:100%;text-align:left;background:none;border:0;
  padding:9px 14px;cursor:pointer;font:inherit;color:#1e232a}
.fbrow>button.fbhead:hover{background:#f4f6f8}
.fbrow.open>button.fbhead{background:#f4f6f8}
.fbhead .fbtitle{display:block;font:600 12.5px var(--body);
  white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.fbhead .fbmeta{display:block;margin-top:2px;font:11px var(--mono);color:#79828c}
.fbpill{display:inline-block;border-radius:3px;padding:0 5px;margin-right:6px;
  font:600 10px var(--mono);text-transform:uppercase;letter-spacing:.03em;vertical-align:1px}
.fbpill.open{background:#fdf1e6;color:#9a5a1e}
.fbpill.fixed{background:#e8f2ea;color:var(--ok)}
.fbpill.archived{background:#eef1f4;color:#79828c}

.fbbody{padding:2px 14px 12px;border-top:1px dashed #e6eaee}
.fbdetail{white-space:pre-wrap;font:12.5px/1.5 var(--body);color:#3a424b;margin:8px 0}
.fbfacts{margin:8px 0 0;font:11px/1.7 var(--mono);color:#79828c}
.fbfacts b{color:#4a545e;font-weight:600}
.fbacts{display:flex;gap:6px;flex-wrap:wrap;margin-top:10px}
.fbacts button{background:#fff;border:1px solid #dfe4ea;border-radius:4px;padding:5px 9px;
  font:12px var(--body);color:#4a545e;cursor:pointer}
.fbacts button:hover{background:#eef1f4;color:#1e232a}
.fbacts button.pri{background:#e8f2ea;border-color:#c6dfcb;color:#31663a;font-weight:600}
.fbacts button.pri:hover{background:#daebde}
.fbacts button.del{margin-left:auto;color:#a03434;border-color:#f0c9c9;background:#fdf2f2}
.fbacts button.del:hover{background:#f9e3e3}
.fbtrace{margin-top:9px;max-height:220px;overflow:auto;background:#1d1f22;color:#cfd4da;
  border-radius:5px;padding:8px 10px;font:10.5px/1.5 var(--mono);white-space:pre}
.fbgone{margin-top:8px;font:11px var(--mono);color:#9a5a1e;background:#fdf1e6;
  border-radius:4px;padding:6px 8px;line-height:1.5}

/* ── Sign-in page (web/login.html) ─────────────────────────────────────────
   Replaces the popup bridge.js used to build inline. That panel carried its
   own palette (#1f6feb, system-ui, a white card) and matched nothing else in
   the product; everything here is the same tokens the app uses, so the page
   reads as the front door of Kitchen Studio Pro rather than a gate in front of
   it. The app header is reused verbatim in the markup.

   body is scoped with .loginpage because the app's own body rule is
   height:100vh + overflow:hidden for the drawing board, which would clip a
   centred card on a short window. */
body.loginpage{overflow:auto}
.loginmain{flex:1;display:flex;align-items:center;justify-content:center;
  padding:28px 18px 64px;min-height:0}
.logincard{width:100%;max-width:352px;background:var(--card);
  border:1px solid var(--line);border-radius:3px;padding:26px 26px 22px;
  box-shadow:0 8px 24px rgba(0,0,0,.07),0 2px 6px rgba(0,0,0,.05)}
.loginttl{font-family:var(--display);font-size:19px;font-weight:700;
  letter-spacing:-.01em;color:var(--ink);margin-bottom:3px}
.loginsub{font-size:12.5px;color:var(--slate);margin-bottom:19px}
.logincard .fld{margin-bottom:13px}
.logincard input{padding:8px 9px;font-size:13px}
.logincard button{width:100%;margin-top:3px;padding:9px 13px;font-size:13px}
.logincard button:disabled{background:var(--slate);border-color:var(--slate);
  cursor:default}
/* min-height reserves the line so submitting does not jump the button down by
   one row the first time it fails. */
.loginerr{color:var(--warn);font-size:12px;line-height:1.35;min-height:17px;
  margin:2px 0 9px}
.loginfoot{font-size:11.5px;color:var(--slate);margin-top:15px;text-align:center}
.loginpage #verstamp{font-family:var(--mono);font-size:9px;color:#82868c}

/* Signed-in account + sign-out, far right of the header. Styled against the
   header's charcoal rather than the page: the global button rule is ink-on-
   paper, which is invisible on #1d1f22. Mirrors .navmode's treatment. */
.whoami{margin-left:auto;display:flex;align-items:center;gap:10px;flex-shrink:0}
.whoami .whoname{font-family:var(--mono);font-size:11px;color:#9aa0a7;
  white-space:nowrap;max-width:190px;overflow:hidden;text-overflow:ellipsis}
.whoami button{display:flex;align-items:center;gap:7px;border:1px solid #33373c;
  border-radius:3px;background:transparent;color:#d2d5d9;font-family:var(--body);
  font-size:12px;font-weight:600;padding:5px 10px 5px 12px;cursor:pointer}
.whoami button:hover{background:#282b30;color:#fff;border-color:#41464c}
.whoami button[aria-expanded="true"]{background:#282b30;color:#fff;border-color:#41464c}
.whoami button:disabled{opacity:.55;cursor:default;background:transparent}
.whoami .whocaret{font-size:9px;line-height:1;opacity:.75}

/* Profile / Admin dialog. Built on the recolour popup's .rcmask/.rcbox so it
   is the same modal the app already had, not a fourth one. */
.pfbox{width:min(380px,100%)}
.pfbox .rcmid{padding:14px 16px 6px}
.pfrow{display:flex;align-items:baseline;gap:10px;margin-bottom:7px;font-size:12.5px}
.pfrow .pfk{width:74px;flex-shrink:0;font-size:9.5px;font-weight:600;
  letter-spacing:.07em;text-transform:uppercase;color:var(--slate)}
.pfrow .pfv{font-family:var(--mono);color:var(--ink);word-break:break-all}
.pfsep{border-top:1px solid var(--soft);margin:14px 0 12px}
.pfhd{font-family:var(--display);font-size:10.5px;font-weight:600;
  letter-spacing:.09em;text-transform:uppercase;color:var(--slate);margin-bottom:10px}
.pfnote{font-size:12.5px;color:var(--slate);line-height:1.5;margin-bottom:10px}
.pfnote code{font-family:var(--mono);font-size:11.5px;background:var(--paper);
  padding:1px 5px;border-radius:3px}
/* min-height reserves the line so the button does not jump on the first error. */
.pfmsg{font-size:12px;line-height:1.35;min-height:16px;margin:2px 0 4px;
  color:var(--warn)}
.pfmsg.ok{color:var(--ok)}

/* ---- admin screen -------------------------------------------------------
   Built on the same .rcmask/.rcbox pieces the recolour popup and the profile
   dialog use — the admin panel is a wider .rcbox, not a fourth kind of modal.
   The sub-dialogs it opens are plain profileDialog()s on top of it. */
.admbox{ width:min(920px, 94vw); max-height:86vh; }
.admbox .rcmid{ overflow:auto; max-height:66vh; }
.admtools{ margin:0 0 10px; }
.admtree{ border:1px solid var(--line); border-radius:8px; overflow:hidden; }

.admrow{
  display:flex; align-items:center; gap:8px; flex-wrap:wrap;
  padding:7px 10px; border-bottom:1px solid var(--line); font-size:12px;
}
.admrow:last-child{ border-bottom:0; }
.admnode{ background:var(--panel2, rgba(0,0,0,.03)); font-weight:600; }
.admuser{ font-weight:400; }
/* Inactive is a state, not an error: dimmed and labelled, never red. A red row
   reads as "something is broken" for what is usually a deliberate act. */
.admrow.off{ opacity:.55; }
/* The row a create just made, so it can be found in a long tree. */
.admrow.admnew{ outline:2px solid var(--accent, #7B1420); outline-offset:-2px; }
.admstray{ background:transparent; font-style:italic; }

.admname{ flex:0 1 auto; min-width:120px; }
.admlogin{ color:var(--slate); font-family:ui-monospace,Menlo,Consolas,monospace; }
.admkind{ color:var(--slate); text-transform:lowercase; }
.admbadge{
  border:1px solid var(--line); border-radius:999px; padding:1px 7px;
  font-size:10px; color:var(--slate); white-space:nowrap;
}
.admseats{ margin-left:auto; color:var(--slate); white-space:nowrap; }
/* Over the ceiling is a fact to show, never a block: section 12 is explicit
   that seats are self-service with no blocking at the ceiling. */
.admseats.over{ color:#b4643c; font-weight:600; }
.admacts{ display:flex; gap:4px; flex:0 0 auto; }
.admacts button{ font-size:11px; padding:3px 8px; }

.admmsg{ min-height:16px; margin:0 0 8px; font-size:12px; }
.admmsg.ok{ color:var(--slate); }
.admmsg.bad{ color:#b4643c; }
.admscope{ margin-right:auto; color:var(--slate); font-size:11px; }
.admchk{ display:flex; align-items:center; gap:7px; margin:6px 0; font-size:12px; }
/* The global input{width:100%} stretched the box across the dialog and pushed
   its label to the far edge (s11 console walk, 2026-10-09). */
/* Ten console tabs plus the account block are wider than a 1366-1440px laptop
   with the stock tab padding: the page scrolled sideways and "Sign out" sat
   past the edge (s11 console walk, 2026-10-09). Tighter below 1500px. */
@media (max-width:1500px){
  #consTabs button{ padding-left:9px; padding-right:9px; font-size:12px; }
  header:has(#consTabs){ gap:10px; padding-left:12px; padding-right:12px; }
}
.admchk input[type=checkbox]{ width:auto; flex:none; margin:0; padding:0; }

/* "New design" sits between Save and the list: starting fresh is a sibling of
   saving, not one of the saved rows. */
.dsgnnew{ padding:0 10px 8px; }
.dsgnnew button{ width:100%; font-size:11px; padding:5px 8px; }

/* Which half of the dealership pool the Designs panel is showing. Built from
   the same pieces as the rest of the popup rather than a new visual language;
   .on is the selected half. */
.dsgnscope{ display:flex; gap:4px; padding:8px 10px 6px; }
.dsgnscope button{ flex:1; font:11px inherit; padding:4px 6px; cursor:pointer;
                   border:1px solid #dfe4ea; border-radius:5px;
                   background:#fff; color:#5b6470; }
.dsgnscope button:hover{ background:#f4f6f8; }
.dsgnscope button.on{ background:var(--ink,#2c3440); border-color:var(--ink,#2c3440);
                      color:#fff; font-weight:600; }

/* ---- stub landing page (/partner) --------------------------------------- */
.landing{ max-width:640px; margin:0 auto; padding:28px 20px 40px; }
.landing h1{ margin:0 0 6px; }
.landing .lead{ color:var(--slate); margin:0 0 18px; }
.landing p{ line-height:1.55; margin:0 0 14px; }
.landing .links{ display:flex; align-items:center; gap:14px; margin:22px 0; }
.landing .note{ color:var(--slate); font-size:12px; border-top:1px solid var(--line);
                padding-top:12px; }


/* ---- the admin console (/console) ----------------------------------------
   The screen that replaces `python3 -m app.usercli`. It reuses the app's own
   pieces wherever one exists — .navmode for the tab strip, .whoami for the
   account block, .admrow/.admbadge for the tree, .rcmask/.rcbox for the
   dialogs — so it reads as the same product and not as an admin tool bolted
   on beside it. Only the layout below is new.

   body scrolls, like .loginpage: the app's own body rule is height:100vh +
   overflow:hidden for the drawing board, which would clip a long tree or a
   thousand audit rows. */
body.conspage{ overflow:auto; }
/* display:block RESETS the bare `main{display:grid}` rule at the top of this
   file, which is the DESIGNER's sidebar-plus-stage layout and reaches the
   console only because that selector is an element name. The console then had
   a hidden two-column grid nobody declared, and it worked by luck: `hidden`
   is display:none, so only two children were ever placed — the message line
   in column one and the single visible pane in column two.

   Adding a THIRD visible child (the expired-agreement banner, ui-0.36.0) put
   the pane back into the 264px column, where it rendered one word per line.
   The bug was in how many children happened to be visible, which is not a
   thing anyone editing console.html can be expected to keep in mind. */
.consmain{ display:block; flex:1; min-height:0; padding:16px 20px 48px;
  overflow:auto; }
.conspage .whoami .conslink{ color:#9aa0a7; font-size:12px; font-weight:600;
  text-decoration:none; padding:5px 8px; border-radius:3px; }
.conspage .whoami .conslink:hover{ color:#fff; background:#282b30; }
.conspage #verstamp{ font-family:var(--mono); font-size:9px; color:#82868c; }

.conshd{ display:flex; align-items:baseline; gap:12px; flex-wrap:wrap;
  margin:0 0 12px; }
.conshd h1{ font-family:var(--display); font-size:17px; font-weight:700;
  letter-spacing:-.01em; margin:0; }
.consscope{ color:var(--slate); font-size:12px; }
.consacts{ margin-left:auto; display:flex; gap:6px; align-items:center;
  flex-wrap:wrap; }
.consh2{ font-family:var(--display); font-size:15px; margin:0 0 10px; }
.consh3{ font-family:var(--display); font-size:10.5px; font-weight:600;
  letter-spacing:.09em; text-transform:uppercase; color:var(--slate);
  margin:18px 0 8px; }
/* min-height reserves the line so the first message does not shove the whole
   tree down by one row. */
.consmsg{ min-height:18px; margin:0 0 10px; font-size:12.5px; }
.consmsg.ok{ color:var(--slate); }
.consmsg.bad{ color:var(--warn); }
.consfilter{ width:auto; min-width:120px; font-size:11.5px; padding:5px 7px; }

/* The account number, in mono and never folded into the name: it is the
   immutable customer-facing key, and a name is neither unique nor stable. */
.consacct{ font-family:var(--mono); font-size:10.5px; color:var(--slate);
  flex-shrink:0; }
.conslast{ color:var(--slate); font-size:10.5px; margin-left:auto;
  white-space:nowrap; }
.consbody .admrow{ flex-wrap:wrap; }
/* Seats become an edit affordance only when the caller may set them — the
   attribute is absent otherwise, so this cannot advertise a control that
   would only 403. */
.admseats[data-seats]{ cursor:pointer; border-bottom:1px dashed var(--line); }
.admseats[data-seats]:hover{ color:var(--ink); border-bottom-color:var(--ink); }
.consseatedit{ display:inline-flex; gap:4px; align-items:center; }
.consseatedit input{ width:88px; padding:3px 6px; font-size:11.5px; }
.consseatedit button{ font-size:11px; padding:3px 8px; }

/* Entitlements: the organisation list stays put while the detail scrolls, so
   moving between dealerships does not lose your place in the tree. */
.conssplit{ display:grid; grid-template-columns:minmax(180px,260px) minmax(0,1fr);
  gap:18px; align-items:start; }
@media (max-width:820px){ .conssplit{ grid-template-columns:minmax(0,1fr); } }
.conspick{ border:1px solid var(--line); border-radius:8px; overflow:hidden;
  background:var(--card); position:sticky; top:0; }
/* Section 4: the brand-wide row under the per-line table, and the add-a-type
   row. Both are "one control that does the common thing", set apart from the
   table above so they do not read as another row of it. */
.costwide{ display:flex; align-items:center; gap:7px; flex-wrap:wrap;
  margin:10px 0 4px; padding:8px 9px; border:1px solid var(--line);
  border-radius:6px; background:var(--soft); font-size:12px; }
.costwide input{ width:90px; }
.costwide select{ min-width:120px; }
.costin,.ctin{ width:80px; }

.conspickrow{ display:flex; align-items:center; gap:8px; width:100%;
  border:0; border-radius:0; border-bottom:1px solid var(--line);
  background:transparent; color:var(--ink); font-weight:500; font-size:12px;
  text-align:left; padding:7px 10px; }
.conspickrow:last-child{ border-bottom:0; }
.conspickrow:hover{ background:var(--soft); color:var(--ink); }
.conspickrow.on{ background:var(--slate); color:var(--paper); }
.conspickrow.on .consacct{ color:var(--paper); opacity:.75; }
.conspickrow.off{ opacity:.55; }
.conspickname{ flex:1 1 auto; overflow:hidden; text-overflow:ellipsis;
  white-space:nowrap; }
.consdetail{ min-width:0; }
.consceil{ border-left:3px solid var(--sage); padding-left:10px; }

.consents{ border:1px solid var(--line); border-radius:8px; overflow:hidden; }
.consbrand{ display:flex; align-items:center; gap:8px; padding:7px 10px;
  background:var(--soft); font-weight:600; font-size:12.5px; }
.consline{ display:flex; align-items:baseline; gap:10px; padding:6px 10px;
  border-top:1px solid var(--line); font-size:12px; flex-wrap:wrap; }
.conslname{ min-width:150px; color:var(--slate); }
.consfins{ display:flex; gap:4px; flex-wrap:wrap; }
/* Inherited and granted-here are told apart by FILL, not by colour alone:
   which of the two a finish is decides whether it can be revoked here, and
   that has to survive a monochrome screen and a colour-blind reader. */
.consfin{ font-size:10.5px; padding:1px 6px; border-radius:9px;
  border:1px dashed var(--line); color:var(--slate); }
.consfin.here{ border-style:solid; border-color:var(--sage);
  background:var(--sage); color:#fff; }
.consleg{ display:flex; gap:8px; align-items:center; margin:8px 0 0;
  font-size:11px; color:var(--slate); }

.consgrants{ border:1px solid var(--line); border-radius:8px; overflow:hidden; }
.consgrant{ display:flex; align-items:center; gap:10px; padding:6px 10px;
  border-bottom:1px solid var(--line); font-size:12.5px; }
.consgrant:last-child{ border-bottom:0; }
.consgrant.dangling{ background:#f6ece7; }
.consglabel{ font-weight:600; }
.consgby{ color:var(--slate); font-size:11px; margin-left:auto; }
.consgrant button{ font-size:11px; padding:3px 8px; }
.consgrantform{ display:flex; gap:6px; align-items:center; flex-wrap:wrap;
  margin:10px 0; }
.consgrantform select{ width:auto; min-width:150px; font-size:12px; }

.conssub{ border:1px solid var(--line); border-radius:8px; overflow:hidden; }
.conssubrow{ display:flex; gap:10px; padding:6px 10px; font-size:12px;
  border-bottom:1px solid var(--line); }
.conssubrow:last-child{ border-bottom:0; }
.conssubname{ min-width:160px; font-weight:600; }
.conssubwhat{ color:var(--slate); }
.conssubown{ margin-left:auto; color:var(--slate); font-size:11px; }

/* The audit log. A table, deliberately: these rows are read by scanning one
   column down the page — "everything that happened to this dealership", "every
   password reset" — and a stack of cards makes that the one thing it cannot
   do. It scrolls inside its own box rather than widening the page. */
.constable{ width:100%; border-collapse:collapse; font-size:12px;
  background:var(--card); border:1px solid var(--line); border-radius:8px;
  display:block; overflow-x:auto; }
.constable th{ text-align:left; font-family:var(--display); font-size:9.5px;
  font-weight:600; letter-spacing:.08em; text-transform:uppercase;
  color:var(--slate); padding:7px 9px; border-bottom:1px solid var(--line);
  white-space:nowrap; }
.constable td{ padding:5px 9px; border-bottom:1px solid var(--soft);
  vertical-align:top; }
.constable tr:last-child td{ border-bottom:0; }
.constable .consmono{ font-family:var(--mono); font-size:11px; }
.constable .consw{ white-space:nowrap; color:var(--slate); }
.constable .consdet{ font-family:var(--mono); font-size:10.5px;
  color:var(--slate); max-width:280px; word-break:break-word; }

/* ---- label prices (console, section 3) -----------------------------------
   A table, and edited in place: this is read down a column — "what did the
   base cabinets go up to" — and a modal per cell would make one price a
   two-click round trip through a dialog that hides the column it came from. */
.conspdesc{ color:var(--slate); font-size:11.5px; max-width:260px; }
.conspcell{ white-space:nowrap; font-family:var(--mono); font-size:12px; }
.conspcell[data-price]{ cursor:pointer; }
.conspcell[data-price]:hover .conspval{ border-bottom-color:var(--ink); }
.conspcell[data-price]:focus-visible{ outline:2px solid var(--sage);
  outline-offset:-2px; }
.conspval{ border-bottom:1px dashed transparent; }
/* Set-here is bold AND tagged, never colour alone: which of the three states a
   price is in decides whether it can be reverted, and that has to survive a
   monochrome print and a colour-blind reader. */
.conspcell.set .conspval{ font-weight:700; color:var(--ink); }
.conspsrc{ margin-left:6px; font-family:var(--body); font-size:9px;
  font-weight:700; letter-spacing:.04em; text-transform:uppercase;
  padding:1px 5px; border-radius:8px; background:var(--sage); color:#fff; }
.conspsrc.rev{ background:transparent; color:var(--warn);
  border:1px solid var(--warn); }
.conspedit{ display:inline-flex; gap:4px; align-items:center; }
.conspedit input{ width:104px; padding:3px 6px; font-size:11.5px; }
.conspedit button{ font-size:11px; padding:3px 7px; }
.consmsg.bad, .pfnote.bad{ color:var(--warn); }

/* Profile two-factor section + enrolment dialog (Phase 3c) */
.pf2fa{font-size:13px}
.pf2sub{color:var(--slate,#5a6572);font-size:12px;line-height:1.5;margin:2px 0 8px}
.pf2ok{color:#1a7f4b;font-weight:600}
.pf2qrwrap{display:flex;justify-content:center;margin:6px 0}
.pf2qr{width:180px;height:180px;image-rendering:pixelated;border:1px solid var(--line,#cfd6e0);border-radius:6px;background:#fff}
.pf2key{display:flex;flex-wrap:wrap;align-items:center;gap:6px;font-size:12px;margin:6px 0}
.pf2key code{font-family:ui-monospace,Menlo,monospace;background:#f1f4f8;padding:3px 6px;border-radius:5px;letter-spacing:1px;user-select:all}
.pf2codes{font-family:ui-monospace,Menlo,monospace;font-size:13px;line-height:1.7;background:#f1f4f8;border:1px solid var(--line,#cfd6e0);border-radius:6px;padding:10px 14px;margin:8px 0;user-select:all;white-space:pre}

/* ---- COMING SOON ---------------------------------------------------------
   A corner ribbon, NOT a band across the middle.

   The band version escaped the control it belonged to: rotated, nowrap and
   centred, it was wider and taller than a 24px toolbar button, so on
   "Design it for me" it rode up over the header bar and was clipped by it,
   and on the toolbar it lay across the neighbouring Fill button. Both ends
   unreadable, and the label underneath obscured.

   Three things fix it, all load-bearing:
     * `overflow:hidden` on the CONTROL, so the ribbon can never paint outside
       its own bounds however the row re-flows;
     * a CORNER flag rather than a centred band, so the label keeps the middle
       of the control to itself;
     * a short word and a translucent fill, so what it does cover stays
       readable through it.

   `position:relative` resolves the absolute child against the control;
   `overflow:hidden` on that same element is what clips it. */
.soon{ position:relative; overflow:hidden; cursor:not-allowed; opacity:.85;
       padding-right:1.15em; }
.soon::after{
  content:'SOON';
  position:absolute; top:0; right:0;
  transform:translate(29%, -32%) rotate(38deg);
  transform-origin:center;
  min-width:66px; text-align:center;
  font:800 6.5px/1.7 var(--sans,inherit); letter-spacing:.7px;
  color:#fff; background:rgba(183,48,48,.82);
  box-shadow:0 0 0 1px rgba(255,255,255,.28) inset;
  pointer-events:none;   /* never eat a click; `disabled` is what blocks it */
}
.soon:disabled{ opacity:.85; }

/* ---- THE FILE MENU -------------------------------------------------------
   A desktop menu, in Notepad's shape. Three columns on every row — check
   rail, label, shortcut — because the shortcuts only line up down the right
   if every row emits all three cells, including the rows that use none of
   them. Uniform height, single-line labels, no two-line rows. */
.filemenu{ position:fixed; z-index:99998; min-width:238px; padding:4px 0;
           background:#fff; color:var(--ink,#2c3440);
           border:1px solid var(--line,#dfe4ea); border-radius:5px;
           box-shadow:0 8px 26px rgba(0,0,0,.17); font-size:12.5px; }
.filemenu .fmitem{
  display:grid; grid-template-columns:20px 1fr auto; align-items:center;
  gap:8px; width:100%; height:26px; padding:0 12px 0 4px;
  border:0; background:none; text-align:left; cursor:pointer;
  font:inherit; color:inherit; white-space:nowrap; }
.filemenu .fmitem:hover{ background:var(--sage,#4a7a52); color:#fff; }
/* The left rail holds the checkmark. It is always present, so a checkable row
   is the same height and the same indent as every other row. */
.filemenu .fmcheck{ width:20px; text-align:center; font-size:11px;
                    opacity:.9; }
.filemenu .fmlabel{ overflow:hidden; text-overflow:ellipsis; }
/* Right-aligned, muted, smaller than the label. */
.filemenu .fmkey{ justify-self:end; font-size:10.5px; opacity:.55;
                  font-variant-numeric:tabular-nums; letter-spacing:.2px; }
.filemenu .fmitem:hover .fmkey{ opacity:.8; }
.fmsep{ height:1px; margin:4px 8px; background:var(--line,#dfe4ea); }

/* ---- New / Save As dialog ------------------------------------------------ */
.dlgmask{ position:fixed; inset:0; z-index:99999; display:flex;
          align-items:center; justify-content:center;
          background:rgba(18,20,24,.35); }
.dlgbox{ width:340px; max-width:92vw; background:#fff; border-radius:7px;
         box-shadow:0 18px 48px rgba(0,0,0,.3); padding:16px 18px 14px; }
.dlghd{ font-size:14px; font-weight:700; margin-bottom:12px; }
.dlgrow{ display:block; margin-bottom:10px; }
.dlgrow span{ display:block; font-size:11px; color:var(--slate,#5b6470);
              margin-bottom:3px; }
.dlgrow input{ width:100%; box-sizing:border-box; font:inherit; font-size:12.5px;
               padding:5px 8px; border:1px solid var(--line,#dfe4ea);
               border-radius:4px; }
.dlgrow input:focus{ outline:2px solid var(--sage,#4a7a52); outline-offset:-1px; }
.dlgacts{ display:flex; justify-content:flex-end; gap:8px; margin-top:14px; }
/* flex:0 0 auto because the primary button is .wpb, which is flex:1 so it
   can share a wall-panel row; in a dialog that stretched it across the box.
   This selector outranks .wpb, so the wall panel is untouched. */
.dlgacts button{ font-size:12px; padding:5px 14px; border-radius:4px;
                 cursor:pointer; flex:0 0 auto; }

/* The .ksp local copy: the one-time explanation and the ONE quiet line a
   failed copy leaves. Quiet means small, bottom-left, out of the drawing and
   never modal; it is replaced, never stacked. */
.kspdlgtext{ font-size:12.5px; line-height:1.45; }
.kspdlgtext p{ margin:0 0 8px; }
.dlgbox:has(.kspdlgtext){ width:420px; }
.ksplocal{ position:fixed; left:12px; bottom:12px; z-index:9000;
           display:flex; align-items:center; gap:8px; max-width:calc(100vw - 24px);
           background:#fff; border:1px solid var(--line,#d9dde3); border-radius:5px;
           padding:5px 8px; font-size:11.5px; color:var(--slate,#5b6470);
           box-shadow:0 2px 8px rgba(0,0,0,.08); }
.ksplocal button{ font-size:11.5px; padding:2px 8px; border-radius:4px;
                  cursor:pointer; background:none; border:1px solid var(--line,#d9dde3); }
.ksplocal #kspLineX{ border:none; padding:2px 4px; }

/* Customer · job on an Open row. Its own line, omitted when both are blank. */
.dsgncj{ display:block; font-style:normal; font-size:11px;
         color:var(--sage,#4a7a52); font-weight:600;
         overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }

/* ---- TOOLBAR TOOLTIPS ----------------------------------------------------
   A custom bubble, not `title=`: the native one cannot be styled, cannot be
   switched off, and waits about a second. Above the toolbar, below any modal
   (.dlgmask is 99999). Never interactive — it must not sit between the cursor
   and the button it describes. */
.tiptip{
  position:fixed; z-index:99990; max-width:260px;
  padding:6px 9px; border-radius:5px;
  background:rgba(24,27,32,.96); color:#f2f4f6;
  font:12px/1.45 var(--sans,inherit); font-weight:500;
  box-shadow:0 6px 18px rgba(0,0,0,.28);
  pointer-events:none; }
.tipopt{ display:flex; flex-direction:row; align-items:center; gap:8px;
         font-size:12px; padding:2px 0 4px; margin:0; cursor:pointer; }
.tipopt input{ cursor:pointer; margin:0; flex:0 0 auto; }
.tipopt span{ flex:1 1 auto; text-align:left; }

/* Color settings: a swatch with its own reset. */
#colorSettings .throw{ display:flex; flex-wrap:wrap; gap:10px 12px; margin-bottom:4px; }
#colorSettings .thchip{ display:flex; flex-direction:column; align-items:center;
  gap:3px; width:52px; text-align:center; font-size:11px; position:relative; }
#colorSettings .thc{ width:36px; height:24px; padding:0; cursor:pointer;
  border:1px solid var(--line); border-radius:5px; }
#colorSettings .thchanged .thname{ font-weight:700; }
#colorSettings .thchanged::after{ content:''; position:absolute; top:-3px; right:4px;
  width:7px; height:7px; border-radius:50%; background:var(--clay); }
#colorSettings .thrs{ border:0; background:none; padding:0 4px; cursor:pointer;
  font-size:13px; line-height:1; color:var(--ink); }
#colorSettings .thrs:disabled{ opacity:.25; cursor:default; }

/* ---- APPLIANCE PICKER ----------------------------------------------------
   The two selects were raw native controls sitting beside a styled button, so
   they rendered differently on every OS. Same border, radius and type scale as
   the rest of the panel; the arrow is drawn rather than left to the platform. */
.applrow{ display:flex; gap:5px; align-items:center; flex-wrap:wrap; }
.applsel{
  flex:1 1 130px; min-width:0; font:12px/1.3 var(--sans,inherit);
  padding:4px 22px 4px 8px; border:1px solid var(--line,#dfe4ea);
  border-radius:4px; background:#fff; color:var(--ink,#2c3440);
  -webkit-appearance:none; -moz-appearance:none; appearance:none;
  background-image:linear-gradient(45deg,transparent 50%,#8a939e 50%),
                   linear-gradient(135deg,#8a939e 50%,transparent 50%);
  background-position:calc(100% - 13px) 52%, calc(100% - 8px) 52%;
  background-size:5px 5px, 5px 5px; background-repeat:no-repeat; }
.applsel:focus{ outline:2px solid var(--sage,#4a7a52); outline-offset:-1px; }
.applsel optgroup{ font-weight:700; color:var(--slate,#5b6470); }
.applsel option{ font-weight:400; color:var(--ink,#2c3440); }
.applcfg{ flex:0 1 120px; }
.applcfg[hidden]{ display:none; }
.applbtn{ flex:0 0 auto; }

/* Diagnostics (ui-0.53.1): developer tools stay hidden until Diagnostics is
   opened, and Diagnostics exists only with ?debug=1. CSS, so nothing flashes
   before the page script runs. */
body:not(.diag) [data-diag]{display:none!important}
body:not(.debug) #diagBtn{display:none!important}

/* ===== BUTTON HIERARCHY (ui-0.54.0) ======================================
   Render, Super 3D, Fill, Feedback and Select all carried the same weight, so
   nothing read as primary. Now:
     - ONE accent colour, used ONCE: the Order tab (Nick, 2026-09-13) — the
       path from a design to what gets ordered;
     - secondary (.t2): outline — undo/redo, zoom, Designs, Objects, Feedback,
       Render;
     - tertiary (.t3): neutral grey — Magnet, Move whole run, Coords, Dims,
       Select, and the diagnostic tools;
     - the Potential issues badge is the only other colour: green at zero, red
       when something blocks.
   Scoped to the header, the toolbar and the plan overlay. The global `button`
   rule and --clay style every panel in the app; repainting those was not the
   item. tests/test_renderer_js.py::test_one_accent_and_a_button_hierarchy. */
:root{ --accent:#185FA5; --t3-ink:#5f6670; --t3-line:#cfd3d8; --t3-on:#e2e5e9; }
header{border-bottom-color:#33373c}
.navmode button.on{background:#3a3f45;color:#fff}
.navmode #orderTab{background:var(--accent);color:#fff}
.navmode #orderTab:hover{background:var(--accent);color:#fff;filter:brightness(1.12)}
.navmode #orderTab.on{background:var(--accent);color:#fff;box-shadow:inset 0 -3px 0 rgba(255,255,255,.75)}
.seg2 button:hover:not(.on){background:var(--t3-on);color:var(--ink)}
/* the active VIEW is a state, not an action: dark grey like the active tab.
   It was var(--ink), which applyTheme sets to the theme's navy. */
.seg2 button.on{background:#3a3f45;color:#fff}
.rbtools button.t2,.rbtools .rbaction{background:transparent;color:var(--ink);border:1px solid var(--ink);filter:none}
.rbtools button.t2:hover,.rbtools .rbaction:hover{background:var(--ink);color:var(--paper);border-color:var(--ink);filter:none}
.rbtools .rbaction.on,.rbtools .rbaction.on:hover{background:#3a3f45;color:#fff;border-color:#3a3f45;filter:none}
#fbBtn.t2 .fbdot{background:rgba(27,25,23,.14)}
.rbtools button.t3,#gridzoom button.t3{background:transparent;color:var(--t3-ink);border:1px solid var(--t3-line)}
.rbtools button.t3:not(.g),#gridzoom button.t3:not(.g){background:var(--t3-on);color:var(--ink);border-color:#9aa1a9}
.rbtools button.t3:hover,#gridzoom button.t3:hover{background:var(--t3-on);color:var(--ink);border-color:#9aa1a9}
#logbtn{color:var(--t3-ink);border-color:var(--t3-line)}
#logbtn:hover{border-color:#9aa1a9;color:var(--ink)}
.issuesCount{background:var(--ok);color:#fff}
.issuesCount.has{background:#6b7280;color:#fff}
.issuesCount.blocking{background:#c98a0b;color:#fff}

/* ROOM LIST + HEADER ROOM BAR (ui-0.55.0). Neutral, per the button hierarchy:
   the swatch shows the FINISH's colour, which is content, not chrome. */
.roomnav{display:flex;align-items:center;gap:8px;margin-left:6px}
.roomnav[hidden]{display:none}
/* The room DROPDOWN (ui-0.57.0): the room name and a chevron, nothing else. */
header .roombtn{display:inline-flex;align-items:center;gap:6px;background:transparent;border:1px solid #33373c;color:#fff;padding:5px 9px;font-size:12.5px;border-radius:3px}
header .roombtn:hover{background:#282b30;color:#fff;border-color:#4a4f56}
header .roombtn .ti{font-size:13px;color:#aeb3b9}
.rnname{color:#fff;font-size:12.5px;font-weight:600;max-width:170px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.finname{color:#d2d5d9;font-size:12px;white-space:nowrap}
.hdrtotal{color:#fff;font:600 13px var(--mono);white-space:nowrap}
.hdrspacer{margin-left:auto}
.finswatch{display:inline-block;width:18px;height:18px;border-radius:3px;border:1px solid rgba(255,255,255,.55);margin-left:4px}
.obadge{display:inline-block;min-width:16px;padding:0 5px;margin-left:6px;border-radius:8px;background:rgba(255,255,255,.28);color:#fff;font:600 10px var(--mono);line-height:16px;text-align:center;vertical-align:1px}
.obadge[hidden]{display:none}
.rthumb{display:inline-flex;width:40px;height:30px;color:var(--slate);flex:0 0 auto}
.rmtext{display:flex;flex-direction:column;min-width:0;line-height:1.2}
.rmmeta{font-size:10.5px;color:var(--slate);font-weight:400;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
/* The room name sits in a maroon pill with white text (.areahead .ttl): the
   thumbnail and meta take the pill's colour, or slate on maroon is unreadable
   (seen in a browser). The meta wraps rather than being cut to "U-shaped · …". */
.areahead .ttl .rthumb{color:#fff;opacity:.85}
.areahead .ttl .rmtext{flex:1;min-width:0}
.areahead .ttl .rmmeta{color:#fff;opacity:.82;white-space:normal;overflow:visible}

/* LEFT TOOL RAIL and the canvas toolbar's spacer and divider (ui-0.57.0, the
   plan-screen restructure). The rail's tools are neutral like every other
   control; the ACTIVE tool is the one place besides Order that wears the
   accent, as a tint rather than a fill, per the item. */
.rail{display:flex;flex-direction:column;align-items:center;gap:2px;padding:6px 0;
  background:#eef1f5;border-right:1px solid #cfd6e0;min-height:0;overflow-y:auto}
.rail button{display:flex;flex-direction:column;align-items:center;gap:3px;width:52px;
  padding:7px 0 6px;border:1px solid transparent;border-radius:6px;background:transparent;
  color:var(--t3-ink);font-weight:500}
.rail button{width:60px}
.rail button .ti{font-size:19px;line-height:1}
/* The Cabinet tool leads the rail and is the largest button on it. Active, it
   wears the maroon selection colour (outline and text), not the accent tint. */
.rail #railCabinet{width:68px;min-height:76px;justify-content:center;padding:8px 0 7px;gap:5px}
.rail #railCabinet .rlab{font-size:10.5px;font-weight:600}
.rail #railCabinet.on{background:color-mix(in srgb, var(--ksp-selection) 9%, transparent);color:var(--ksp-selection);border-color:var(--ksp-selection)}
.rail .raildiv{width:40px;height:1px;margin:4px 0;background:#cfd6e0;flex:0 0 auto}
.rail .rlab{font-size:9px;line-height:1;letter-spacing:0;white-space:nowrap}
/* The corner SOON ribbon is sized for a header button and, clipped to a 52px
   tool, covered the icon (seen in a browser). A rail tool with nothing behind
   it is disabled and dimmed instead; its tooltip says coming soon. */
.rail button.soon::after{display:none}
.rail button.soon{opacity:.45}
.rail button:hover:not(:disabled){background:var(--t3-on);color:var(--ink);border-color:transparent}
.rail button.on{background:color-mix(in srgb, var(--accent) 14%, transparent);color:var(--accent);border-color:color-mix(in srgb, var(--accent) 35%, transparent)}
.tbgap{margin-left:auto}
.tbdiv{width:1px;align-self:stretch;margin:3px 4px;background:var(--t3-line)}


/* ==================================================== THE RIGHT PANEL ==
   ui-0.59.0: 206px, Selected / Room / Catalog over Design / Other, the Room
   tab as numbered cards, and the issues strip under the canvas. */
#sideL .ptabbar{padding:6px}
#sideL .ptab{font-size:12px;height:28px;padding:0 4px}
#sideL .pb{padding:10px 10px 24px}
#roomBody .acc{border:1px solid #d5dbe4;border-radius:8px;background:#fff;margin:0 0 8px}
/* Space rows are three fixed columns — number + label, value, arrow — so every
   value starts at the same x (job space-panel-rows-aligned-1020; same shape as
   .mouldrow). space-between had centred the value in the gap, a different place
   on every row. minmax(0,1fr) + ellipsis keeps a phone-width row on one line. */
#roomBody .acchead{display:grid;grid-template-columns:150px minmax(0,1fr) 16px;align-items:center;column-gap:8px;padding:9px 10px}
#roomBody .acchead .ttl{font-size:13px;gap:7px;min-width:0;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
#roomBody .acchead .val{font-size:11px;min-width:0;text-align:left;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
#roomBody .acchead .caret{margin-left:0!important;justify-self:end}
#roomBody .accbody{padding:2px 10px 12px;border-top:1px solid #edf0f4}
#roomBody .acc.closed .accbody{display:none}
.acchead .num{display:inline-flex;align-items:center;justify-content:center;width:18px;height:18px;
  border-radius:50%;background:var(--clay);color:#fff;font-size:10.5px;font-weight:700;flex:0 0 auto}

.issuestrip{flex:0 0 auto;display:flex;align-items:center;gap:8px;min-height:30px;padding:4px 10px;
  border-top:1px solid var(--line);background:#fff;font-size:12px;overflow:hidden}
.issuestrip .istext{flex:1;min-width:0;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.issuestrip .ismore{color:var(--slate);white-space:nowrap}
.issuestrip .isok{color:#1f4d2a}
.issuestrip .issev{flex:0 0 auto;padding:0 5px;border-radius:3px;font-size:10px;font-weight:700;text-transform:uppercase}
.issuestrip .issev.blocking{background:#c98a0b;color:#fff}
.issuestrip .issev.review{background:#e8dfc9;color:#6b5418}
.issuestrip button{flex:0 0 auto;padding:2px 10px}
.issuesAll{position:fixed;inset:0;z-index:68;background:rgba(20,26,34,.35);display:flex;
  align-items:center;justify-content:center;padding:16px}
.issuesAll[hidden]{display:none}
.issuesAllBox{background:#fff;border-radius:8px;width:min(560px,100%);max-height:80vh;overflow:auto;
  box-shadow:0 12px 32px rgba(0,0,0,.25)}
.issuesAllBox .issuesHead{display:flex;align-items:center;justify-content:space-between;padding:10px 12px;
  border-bottom:1px solid var(--line)}
.issuesAllBox .issuesBody{padding:8px 12px 12px}

.selnone,.catnote{font-size:12px;color:var(--slate);line-height:1.45;padding:4px 0}
.selcard{font-size:12px}
/* SKU and its wall chip on one line, as the mockup reads: the chip is the
   answer to "which wall is this on", which was a row halfway down a list. */
.selhd{display:flex;align-items:center;gap:8px;margin-bottom:1px}
.selhd .selsku{font-size:17px;letter-spacing:-.01em}
.selchip{margin-left:auto;flex-shrink:0;font-size:10.5px;font-weight:600;
  padding:3px 8px;border-radius:10px;background:rgba(107,36,52,.10);
  color:var(--ksp-selection);white-space:nowrap}
/* A field is a LABEL ABOVE a full-width control. Beside-the-control labels are
   what clipped every dropdown in this panel; at any width a 64px label column
   is 64px the value does not get. */
.selfld{margin-bottom:11px}
.selflab{display:block;font-size:9.5px;font-weight:600;letter-spacing:.07em;
  text-transform:uppercase;color:var(--slate);margin-bottom:4px}
.selsel{width:100%;max-width:100%;min-width:0;box-sizing:border-box;
  padding:8px 9px;font-size:12.5px;border:1px solid var(--line);border-radius:4px;
  background:#fff;color:var(--ink);font-family:var(--body)}
.selsel:focus{outline:2px solid var(--sage);outline-offset:-1px}
/* Finish swatches WITH NAMES. They wrap rather than scroll: a line with nine
   finishes must show all nine, and a horizontal scroller hides the ninth. */
.selfins{display:flex;flex-wrap:wrap;gap:8px}
.selfin{background:transparent;border:0;padding:0;cursor:pointer;width:52px;
  display:flex;flex-direction:column;align-items:center;gap:3px}
.selfin:hover{background:transparent}
.selfinsw{width:38px;height:34px;border-radius:5px;border:1px solid var(--line);
  box-shadow:inset 0 0 0 1px rgba(255,255,255,.35)}
.selfin.on .selfinsw{outline:2px solid var(--ksp-selection);outline-offset:1.5px}
.selfinnm{font-size:10px;font-weight:500;color:var(--slate);line-height:1.15;
  text-align:center;word-break:break-word}
.selfin.on .selfinnm{color:var(--ink);font-weight:600}
/* Numbers in rows: W / D / H, then From left / Rotation. */
.selgrid3{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:8px}
.selgrid2{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:8px}
.selgrid3 .selfld,.selgrid2 .selfld{margin-bottom:11px}
.selnum{position:relative}
.selin{width:100%;padding:8px 26px 8px 9px;font-size:12.5px;border:1px solid var(--line);
  border-radius:4px;background:#fff;color:var(--ink);font-family:var(--mono)}
.selin[readonly]{background:#f2efe9;color:var(--slate);cursor:default}
.selunit{position:absolute;right:8px;top:50%;transform:translateY(-50%);
  font-size:11px;color:var(--slate);pointer-events:none}
/* The two facts that are neither a control nor a dimension. */
.selmeta{display:flex;justify-content:space-between;gap:8px;font-size:11.5px;
  color:var(--slate);padding:2px 0}
.selmeta b{font-family:var(--mono);font-size:11.5px;color:var(--ink);font-weight:600}
.selbtns{margin-top:8px}
.selthumb{border:1px solid var(--line);border-radius:6px;background:#f7f8fa;height:118px;display:flex;
  align-items:center;justify-content:center;overflow:hidden;margin-bottom:8px}
.selimg{max-width:100%;max-height:112px}
svg.selimg{width:100%;height:104px;fill:#e3e8ef;stroke:#5a6473;stroke-width:.6}
.selsku{font-family:var(--mono);font-weight:700;font-size:13px}
.seldesc{color:var(--slate);margin:1px 0 8px}
.selrows{display:grid;grid-template-columns:auto 1fr;gap:3px 8px;margin:0 0 10px}
.selrows dt{color:var(--slate)}
.selrows dd{margin:0;text-align:right;font-family:var(--mono);font-size:11.5px;overflow:hidden;text-overflow:ellipsis}
.selbtns{display:grid;grid-template-columns:repeat(3,1fr);gap:4px}
/* the seating icon (layout.js islandSeatingButtonHtml): lit while on */
#selSeat{display:inline-flex;align-items:center;justify-content:center;gap:4px}
#selSeat .seatico{flex:none}
#selSeat.on{outline:2px solid currentColor;outline-offset:-2px}
/* A DOOR'S FLIPS. Deliberately not .selbtns above: that is a 3-up grid sized
   for the cabinet card's short verbs (Duplicate / Spec / Rotate), and these
   labels are whole sentences about what the click will do — "Move the hinge to
   the right". A third of a 360px panel truncates them into two buttons that
   read the same, which is worse than no label. One per row, left-aligned like
   the sentence it is. */
.selflips{display:grid;gap:4px;margin-top:8px}
.selflips button{padding:6px 8px;font-size:11.5px;text-align:left}
.selbtns button{padding:6px 2px;font-size:11.5px}
.selbtns button:disabled{opacity:.45;cursor:not-allowed}
.selwhy{font-size:11px;color:var(--slate);margin-top:4px}
.selsizewarn{font-size:12px;font-weight:600;color:#8a2d1c;background:#fbeae5;
  border-radius:4px;padding:4px 6px;margin-top:4px}
.selisshd{margin:12px 0 6px;font-size:10px;font-weight:600;letter-spacing:.07em;text-transform:uppercase;color:var(--slate)}
.selIss{border-left:3px solid #c9a227;background:#faf7ee;padding:5px 7px;margin-bottom:5px;border-radius:0 4px 4px 0}
.selIss.blocking{border-left-color:#c98a0b;background:#fdf6e6}
.selIssM{font-weight:600}
.selIssF{color:var(--slate);margin-top:2px}
.selIssNone{color:var(--slate)}

#pcat #catQ{margin-bottom:6px}
.catchips{display:flex;flex-wrap:wrap;gap:4px;margin-bottom:6px}
.catchip{padding:2px 8px;font-size:11px;border-radius:10px;background:#fff;border:1px solid var(--line);color:var(--slate)}
.catchip.on{background:var(--clay);border-color:var(--clay);color:#fff}
.catcards{display:flex;flex-direction:column;gap:5px}
.cc{border:1px solid var(--line);border-radius:6px;padding:6px 8px;background:#fff;cursor:pointer}
.cc:hover{border-color:var(--clay)}
.cc .cccode{font-family:var(--mono);font-weight:700;font-size:12px}
.cc .ccsize,.cc .cclead{font-size:11px;color:var(--slate)}
.cc .ccprice{font-size:12px;font-weight:600;margin:1px 0}
.cc.off{opacity:.55;background:#f4f5f7}
.cc.off .ccoff{font-size:10.5px;font-weight:700;text-transform:uppercase;color:#5a6473}


/* ========================================= HEADER AND TOOLBAR (ui-0.60.0) ==
   Menus left, brand centred, account right; the toolbar spans the window and
   ends with the room: dropdown, finish swatch, running price. Those were
   styled for the charcoal header (white text); on the light toolbar they take
   the toolbar's own neutral ink. */
/* LEFT zone (ui-0.63.0): File, Design it for me, Specs. It is an inline-size
   CONTAINER, and that has a consequence: size containment gives the element
   no intrinsic width. With justify-self:start it would shrink to nothing. So
   it STRETCHES to its whole minmax(0,1fr) column and lays its buttons out
   from the left with flex. */
.hleft{display:flex;justify-content:flex-start;align-items:center;gap:10px;min-width:0;
  container-type:inline-size;container-name:hleft}
.hleft .hfileacts{margin-left:0}
.hleft .ti{font-size:14px;vertical-align:-2px}
.hleft .hlbl{margin-left:5px;white-space:nowrap}
/* Never wrap a label or its SOON badge onto a second line: a wrap is the zone
   growing taller instead of collapsing, which is how it reaches the brand. */
.hleft button{white-space:nowrap}
/* The brand alone in the middle of the page. */
.hcentre{justify-self:center;display:flex;align-items:center}
/* Order then the account menu, hard against the right edge. */
.hright{justify-self:end;display:flex;align-items:center;gap:10px;min-width:0}
/* THE SPACE / FINISH / PRICE STRIP IS PINNED OVER THE PANEL (ui-1.28.0, Nick).

   It used to be the last item in `.rbtools`, a `flex-wrap:wrap` row, so the
   moment the toolbar ran out of width it dropped onto a SECOND ROW AT THE
   LEFT — the one place it must not be, since it is what you glance at to
   confirm which space and finish you are pricing.

   Now it is absolutely positioned in a band exactly `--panelw` wide, flush
   with the window's right edge, contents centred, so it sits over the
   Space / Design / Other tabs. The band uses the SAME variable as the main
   grid's third column, so the two cannot drift.

   It does NOT follow the panel when that is minimized: "stays fixed" was the
   ask, and a strip that jumps on a toggle is the behaviour being fixed.

   `right:0` and NOT an offset for .roombar's 14px padding: an absolutely
   positioned box measures `right` from its containing block's PADDING BOX,
   whose edge is already the bar's border edge. Correcting for the padding
   pushes the band 14px past the window and puts the strip 13px off the tabs —
   which looks fine in a screenshot and only shows when the two centres are
   measured. */
.rbroom{position:absolute;top:0;bottom:0;right:0;
  width:var(--panelw);box-sizing:border-box;
  display:flex;align-items:center;justify-content:center;gap:8px;
  padding:0 10px;overflow:hidden;border-left:1px solid var(--t3-line)}
.rbroom .roomnav{margin-left:0}
.rbroom .roombtn{display:inline-flex;align-items:center;gap:6px;background:#fff;border:1px solid var(--t3-line);padding:3px 8px;font-size:11.5px;border-radius:3px;color:var(--ink)}
.rbroom .roombtn:hover{background:var(--t3-on);border-color:#9aa1a9}
.rbroom .roombtn .ti{font-size:12px;color:var(--t3-ink)}
.rbroom .rnname{color:var(--ink);font-size:11.5px}
/* Too narrow SHRINKS, it does not move (Nick: "shrink the text or truncate
   the finish name rather than moving it"). The name is the one part that can
   be arbitrarily long, so it is the part that gives. */
.rbroom .finname{color:var(--t3-ink);font-size:11px;min-width:0;
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.rbroom .rnname{max-width:120px}
.rbroom .roomnav{min-width:0}
.rbroom .finswatch{border-color:rgba(0,0,0,.3);width:16px;height:16px;margin-left:0}
.rbroom .hdrtotal{color:var(--ink);font-size:12px}

/* ---- HEADER: collapse before overlap (ui-0.63.0) ----------------------------
   When the left zone is narrower than its buttons at full labels, the labels
   go and the icons stay. It is a CONTAINER query: the question is how wide
   the zone's own 1fr column is, not how wide the window is. A viewport
   breakpoint would be guessing at what the right zone and the brand weigh.
   The threshold is the MEASURED natural width of the three buttons with
   labels and the SOON badge (365px), plus a margin. At 340 the zone was 346px
   wide at a 900px window: too wide to collapse, too narrow to fit. "Design it
   for me" wrapped to two lines, the header grew 49 -> 64px, and the group
   came within 19px of the brand. */
@container hleft (max-width: 390px){
  .hlbl{display:none}
}

/* ---- HEADER SOON: a badge, not a ribbon (ui-0.63.0) ------------------------
   The generic corner ribbon above is clipped by the button's overflow:hidden.
   That keeps it from escaping, but on "Design it for me" it read as a
   cut-off red corner (Nick's screenshot). In the header it is an inline badge
   after the label, inside the button's own box, so there is nothing to clip.
   It must come AFTER the generic .soon::after rule: two checks read that one
   first. */
header .soon{padding-right:12px}
header .soon::after{position:static;transform:none;display:inline-block;min-width:0;
  margin-left:6px;padding:1px 4px;border-radius:3px;vertical-align:1px;
  font:800 8px/1.3 var(--sans,inherit);letter-spacing:.5px;
  color:#fff;background:rgba(183,48,48,.9);box-shadow:none;pointer-events:none}

/* ---- THE CABINET MENU (ui-0.64.0) ------------------------------------------
   One menu for a selected or right-clicked cabinet. It keeps .cabpop's
   position:fixed, scroll and shadow, and lays its actions out the way the
   right-click menu did: a title, then one full-width row per action, and
   Remove in red at the bottom. */
.cabpop.cabmenu{width:250px;padding:6px;display:flex;flex-direction:column;gap:2px}
/* Above the toast (z 9999, bottom centre), which sat over Remove and took its
   click for up to 8s (Nick, 2026-10-03: "i cannot delete with a right
   click"); still under the File menu and the dialogs. */
.cabpop.cabmenu{z-index:10000}
.cabmenu .cmhead{display:flex;align-items:center;gap:6px;padding:2px 2px 6px 8px;
  border-bottom:1px solid var(--soft);margin-bottom:3px}
.cabmenu .cmtitle{flex:1;font:700 13px var(--mono);color:var(--ink)}
.cabmenu .cmx{border:none;background:none;font-size:18px;line-height:1;padding:2px 7px;
  cursor:pointer;color:var(--slate);border-radius:4px}
.cabmenu .cmx:hover{background:#f2efe8;color:var(--ink)}
.cabmenu .cmat{display:flex;align-items:center;gap:4px;padding:4px 8px}
.cabmenu .cmat label{font-size:9px;text-transform:uppercase;letter-spacing:.05em;
  color:var(--slate);margin-right:auto}
.cabmenu .cmat input{width:68px;padding:3px 5px;font:12px var(--mono)}
.cabmenu .cmat .mv{width:24px;height:24px;padding:0;border:1px solid var(--line);
  border-radius:3px;background:#fff;cursor:pointer;color:var(--ink)}
.cabmenu .cmat .mv:hover{border-color:var(--clay);color:var(--clay)}
.cabmenu .cmat .mv:disabled{opacity:.4;cursor:default}
/* facingPosRow (ui-0.91.0) also lands in the door editor, outside .cabmenu.
   From left and From right are two IDENTICAL lines (job 1007-2053): a
   fixed-width label, ◀, the box, ▶, the unit - so the boxes line up one
   under the other, in the menu, the door editor and the box under the plan. */
.cmposwrap{display:flex;flex-direction:column}
.cabmenu .cmat.cmpos,.cmpos{display:flex;align-items:center;gap:4px;padding:3px 8px;flex-wrap:nowrap}
.cmpos label{font-size:9px;text-transform:uppercase;letter-spacing:.05em;
  color:var(--slate);width:62px;flex:none;margin-right:0}
.cmpos input{width:68px;padding:3px 5px;font:12px var(--mono)}
.cmpos .mv{width:24px;height:24px;padding:0;border:1px solid var(--line);
  border-radius:3px;background:#fff;cursor:pointer;color:var(--ink)}
.cmpos .cmu{font:12px var(--mono);color:var(--slate)}
.cmfr,.selfr{font:10px var(--mono);color:var(--slate)}
.selfr{margin:-6px 0 10px}
.cabmenu .cmord{font:12px var(--mono);padding:0 4px}
.cabmenu .cmbtn{text-align:left;font-size:12px;padding:7px 9px;border:none;background:none;
  border-radius:6px;cursor:pointer;color:var(--ink)}
.cabmenu .cmbtn:hover{background:#f2efe8}
.cabmenu .cmsep{height:1px;background:var(--line);margin:3px 4px}
.cabmenu .cmsink{display:flex;align-items:center;gap:8px;padding:4px 9px;font-size:12px}
.cabmenu .cmsink select{flex:1;font-size:12px}
.cabmenu .cmbtn.cmdanger{color:#b3261e;font-weight:600}
/* Remove stays in view when a short window makes the menu scroll. */
.cabmenu .cmbtn.cmdanger{position:sticky;bottom:-6px;background:#fff;
  box-shadow:0 -6px 6px -4px rgba(255,255,255,.9)}
/* A filler's cut width (ui-0.66.0): the number and the slider that used to
   live on its own card, now rows in the one menu. `.cmnote` is the field-cut
   line under them, and the mod-policy refusal when there is no control. */
.cabmenu .cmcut input[type=number]{width:60px}
.cabmenu .cmcutr{padding:0 9px 2px}
.cabmenu .cmcutr input[type=range]{width:100%;margin:0;accent-color:var(--clay)}
.cabmenu .cmnote{padding:2px 9px 6px;font-size:10.5px;line-height:1.35;color:var(--slate)}
.cabmenu .cmbtn.cmdanger:hover{background:#fbeceb}

/* Wall colour picker (ui-0.95.0): 21 chips (plaster + 20 paints) and a custom colour */
.wpsw-grid{display:grid;grid-template-columns:repeat(7,1fr);gap:5px;margin:4px 0 6px}
.wpsw{aspect-ratio:1;min-width:0;border:1px solid var(--line);border-radius:4px;cursor:pointer;padding:0}
.wpsw:hover{outline:2px solid var(--clay);outline-offset:1px}
.wpsw.on{outline:2px solid var(--ink);outline-offset:1px}
.wpsw .wpdef{font:11px var(--mono);color:var(--slate)}
.wpcustom{display:flex;align-items:center;gap:6px;font-size:10px;color:var(--slate)}
.wpcustom input{width:34px;height:22px;padding:0;border:1px solid var(--line);border-radius:3px}
.wplbl{text-transform:none;letter-spacing:0}
.wprow{display:block;margin-top:8px}
.wprow .wpsw-grid{max-width:280px}
.wpgroup{margin-top:4px}
.wpghead{font-size:10px;color:var(--slate);text-transform:none;letter-spacing:0}
.wpfound{margin:4px 0}
.wpfoundrow{display:flex;align-items:center;gap:6px;width:100%;padding:3px 4px;border:1px solid var(--line);border-radius:4px;background:transparent;cursor:pointer;font-size:11px;text-align:left;margin-bottom:3px}
.wpfoundrow.on{outline:2px solid var(--ink)}
.wpfoundrow i{color:var(--slate);font-style:normal;margin-left:auto}
.wpchip{width:16px;height:16px;flex:none;border:1px solid var(--line);border-radius:3px}
.wpmsg,.wpnote{font-size:10.5px;line-height:1.35;color:var(--slate);margin:3px 0}

/* Feedback panel: Send to Claude state (ui-0.96.0) */
.fbclaude{font:11px var(--mono);color:var(--slate);align-self:center}
.fbclaude.ok{color:#2f5f36}
.fbclaude.warn{color:#8a5a00}
.fbclaude.bad{color:#b3261e}

/* FEEDBACK COPY BUTTONS + the bug log's Wrap toggle (ui-1.31.0, Nick).
   Small and quiet: they sit beside the attachment chips and in the actions
   row, and must not compete with Send, which is the button on this form. */
.fbattach .fbcopies{display:inline-flex;gap:4px;margin-left:8px;flex-wrap:wrap}
.fbattach .fbcopy{font-size:10.5px;padding:2px 7px;border:1px solid var(--line);
  background:#fff;border-radius:4px;color:var(--slate);cursor:pointer;
  font-family:var(--label-sans);white-space:nowrap}
.fbattach .fbcopy:hover{border-color:var(--slate);color:var(--ink)}
.fbwrap{display:block;margin:6px 0 4px;font-size:10.5px;padding:2px 7px;
  border:1px solid var(--line);background:#fff;border-radius:4px;
  color:var(--slate);cursor:pointer;font-family:var(--label-sans)}
.fbwrap:hover{border-color:var(--slate);color:var(--ink)}

/* "PULL REPORTS NOW" (ui-1.34.0, Nick). Admin only, and hidden outright for
   everyone else rather than drawn disabled — a control you may never use is
   noise, and the gate is the API either way. The line beside it is the last
   run, which is the whole reason the button is not fire-and-forget. */
.fbpull{display:none;align-items:center;gap:8px;flex-shrink:0;
  padding:6px 10px;background:#f7f9fa;border-bottom:1px solid var(--soft)}
.fbpull.on{display:flex}
.fbpull button{font-size:11px;padding:3px 9px;border:1px solid var(--line);
  background:#fff;border-radius:4px;color:var(--slate);cursor:pointer;
  font-family:var(--label-sans);white-space:nowrap}
.fbpull button:hover:not(:disabled){border-color:var(--slate);color:var(--ink)}
.fbpull button:disabled{opacity:.55;cursor:default}
.fbpull small{font:11px var(--mono);color:#82868c;line-height:1.35;
  min-width:0;overflow-wrap:anywhere}
.fbpull small.bad{color:#b3261e}
/* The staging copy's tools (app/stagingtools.py): the marks list to check
   before sending, and a report's note + tab for Send to Claude. */
.fbmarks{padding:6px 10px;background:#fbfaf6;border-bottom:1px solid var(--soft);
  font-size:12px;flex-shrink:0;max-height:40vh;overflow:auto}
.fbmarks ul{margin:4px 0 6px 16px;padding:0}
.fbmarks i{color:#82868c;font:11px var(--mono)}
.fbmarks button{font-size:11px;padding:3px 9px;margin-right:6px}
.fbmhead{margin:2px 0}
.fbmskip{margin:4px 0;color:#82868c;font-size:11px}
.fbmskip.bad{color:#b3261e}
.fbjob{display:flex;flex-wrap:wrap;gap:6px;align-items:center;width:100%}
.fbjob textarea{flex:1 1 100%;font-size:12px;min-width:0}
.fbjob label{font-size:11px;color:var(--slate)}
/* The log is ONE LINE PER EVENT by default — that is how a trace reads — and
   `wrap` is the escape for when the interesting part has run off a 360px
   panel. Both states keep the monospace: the columns are the point. */
.fbtrace{white-space:pre;overflow-x:auto}
.fbtrace.wrap{white-space:pre-wrap;overflow-wrap:anywhere;overflow-x:hidden}
/* FILE > PRINT: THE PLAN SHEET (web/js/plan-print.js, TARGETS.md section 1).
   A preview window over the app with Print and Close; the sheet itself is
   an SVG, so it prints sharp. EVERY PRINT RULE IS SCOPED TO
   body.printing-plan, which only the preview's Print button sets and which
   is dropped again on afterprint — exactly as the order's Download PDF is
   scoped to body.printing-order — so neither print path can hide the other's
   page. The page size goes in as a <style> for the duration, because @page
   cannot be scoped to a class. */
.ppmask{position:fixed;inset:0;z-index:950;background:rgba(20,18,16,.5);
  display:flex;align-items:center;justify-content:center;padding:16px}
.ppmask[hidden]{display:none}
.ppbox{background:#fff;border-radius:10px;box-shadow:0 18px 50px rgba(0,0,0,.3);
  width:min(1100px,100%);max-height:94vh;display:flex;flex-direction:column;overflow:hidden}
.ppbar{display:flex;align-items:center;gap:10px;padding:10px 14px;
  border-bottom:1px solid var(--line);font-size:13px;color:var(--ink)}
.ppbar .pphint{flex:1;color:var(--slate);font-size:12px}
.ppsheet{overflow:auto;padding:14px;background:#e9e7e3}
/* Print options (web/js/print-pack.js): the tick list over the print window */
.ppoptbox{width:min(360px,100%)}
.ppoptbody{padding:12px 16px;display:flex;flex-direction:column;gap:8px}
.ppopt{display:flex;align-items:center;gap:8px;font-size:14px;min-height:28px}
.ppopt input{width:18px;height:18px}
.ppoptall{font-weight:700}
.ppoptsep{border-top:1px solid #ddd}
.ppoptbody .pphint{color:var(--slate);font-size:12px;margin:4px 0 0}
.ppsheet .ppsvg{display:block;width:100%;height:auto;background:#fff;
  box-shadow:0 2px 10px rgba(0,0,0,.18)}
.ppsheet .pppage+.pppage{margin-top:14px}
@media print{
  body.printing-plan > :not(#planprint){display:none !important}
  body.printing-plan,body.printing-plan #planprint{display:block !important;
    position:static !important;inset:auto !important;background:#fff !important;
    padding:0 !important;height:auto !important;overflow:visible !important}
  body.printing-plan .ppbox{box-shadow:none !important;border-radius:0 !important;
    width:auto !important;max-height:none !important;display:block !important}
  body.printing-plan .ppbar{display:none !important}
  body.printing-plan .ppsheet{padding:0 !important;background:#fff !important;
    overflow:visible !important}
  /* By HEIGHT: Letter landscape less the 0.3in margins is 10.4 x 7.9in, and
     the sheet at full width would be 8.04in tall - one blank page after it.
     7.6in, not 7.8: A4 landscape is 8.27in tall, 7.67in inside the margins,
     so the shorter paper sets the height and the one sheet prints whole on
     either (walk 1010 #3; @page stays Letter). */
  body.printing-plan .ppsheet .ppsvg{box-shadow:none !important;width:auto !important;
    height:7.6in !important;max-width:100% !important;margin:0 auto !important;
    break-inside:avoid}
  /* One room per page: a break after every sheet but the last. */
  body.printing-plan .pppage{break-after:page;margin:0 !important}
  body.printing-plan .pppage:last-child{break-after:auto}
}

/* "New version — reload" (plumbing.js showReloadBar). Above everything — a
   dialog mask (.dlgmask, 99999) included, or a page opened onto "Keep a copy"
   cannot reach its own Reload button — and
   it stays until the page reloads: an old page talking to a new engine is not
   something to dismiss. */
#kspReloadBar{position:fixed;top:0;left:0;right:0;z-index:100000;display:flex;
  align-items:center;gap:12px;flex-wrap:wrap;background:#7B1420;color:#fff;
  padding:9px 16px;font:13px/1.4 system-ui,sans-serif;box-shadow:0 2px 10px rgba(0,0,0,.3)}
#kspReloadBar b{font-weight:700}
#kspReloadBtn{margin-left:auto;background:#fff;color:#7B1420;border:0;border-radius:6px;
  padding:5px 14px;font:inherit;font-weight:700;cursor:pointer}

/* THE FIRST PAINT IS THE FINAL PAINT (2026-10-03, Nick: "what is this design
   on the top right shows up for a second when i refresh"). The designer's
   chrome used to be finished by script: applyTheme() (elevinput.js) painted
   the header navy and set the four colour variables, and wirePanelTabs()
   (files.js) hid Areas / Design / Other. Until the script ran, every reload
   showed a near-black header with the old Areas · Design · Other · Order strip
   at the top right. These rules give the designer page — and only it, by
   html[data-shell="app"]; login, console and partner share this file and never
   run applyTheme — the same values from the start. THEME_DEFAULT is the
   source; tests/test_renderer_js.py holds the two equal. */
html[data-shell="app"]{--clay:#1D3A57; --ink:#1D3A57; --card:#f5f3ee; --paper:#eceae5}
html[data-shell="app"] header{background:#1D3A57}
html[data-shell="app"] #mode button[data-m="room"],
html[data-shell="app"] #mode button[data-m="build"]{display:none}
/* The account name's slot is KEPT while /auth/me is answering — invisible,
   not removed — so the Order button is already where it ends up instead of
   starting at the far right and jumping left a fifth of a second later. It
   stays unclickable and shows no empty name (the reason it is hidden). */
html[data-shell="app"] #whoami[hidden]{display:flex !important;visibility:hidden;min-width:96px}

/* one click to one brand (round two) */
.brandfixes{display:flex;flex-wrap:wrap;gap:6px;margin-top:6px}
.ordwarn .brandfix{margin:4px 6px 4px 0}
#rail button.menuon{outline:2px solid #185FA5;outline-offset:-2px}
/* Where a report is (staging copy only; app/stagingtools.py report_status). */
.fbstage{display:inline-block;border-radius:3px;padding:0 5px;font:11px var(--mono);
  background:#eef1f4;color:#4a545e}
.fbstage.s2,.fbstage.s3{background:#eef2fb;color:#2c4a8a}
.fbstage.s4{background:#fdf1e6;color:#9a5a1e;font-weight:600}
.fbstage.s5{background:#f3eefb;color:#5a3c8a}
.fbstage.s6{background:#fff6d6;color:#7a5a00;font-weight:600}
.fbstage.s7{background:#e8f2ea;color:var(--ok)}
.fbstatus{display:flex;flex-direction:column;gap:4px;margin:0 0 8px}
.fbstatus small{color:var(--slate)}
.fbstatus textarea{width:100%;box-sizing:border-box}
.fbq{white-space:pre-wrap;margin:0;padding:6px;background:#fbfaf6;border:1px solid var(--soft);
  font:12px var(--mono);max-height:160px;overflow:auto}
.fbtry{display:flex;flex-wrap:wrap;gap:6px}
.fbcounts{display:flex;flex-wrap:wrap;gap:4px;margin:6px 10px}
.fbmarks li.off{opacity:.55}
.fbmall{display:block;margin:0 0 6px;font-size:12px}
/* Report numbers and the staging copy's toolbar "Fixed" dropdown. */
.fbnum{display:inline-block;min-width:3.4em;margin-right:6px;padding:0 4px;box-sizing:border-box;
  border-radius:3px;background:#1e232a;color:#fff;font:700 12.5px var(--mono);text-align:center}
.fbsort{margin:6px 10px;font-size:11px;color:var(--slate)}
.fbsort button{background:none;border:1px solid var(--soft);border-radius:3px;padding:0 6px;margin-left:4px;font-size:11px;cursor:pointer}
.fbsort button.on{background:#eef2f6;font-weight:600}
.roommenu.fxmenu{width:min(460px,calc(100vw - 12px));max-height:70vh;overflow:auto}
.fxmenu .fxrow{align-items:flex-start;cursor:pointer}
.fxmenu .fxtxt{flex:1;min-width:0}
.fxmenu .fxtxt small{display:block;color:var(--slate);font-size:11px}
.fxmenu .fxok{white-space:nowrap;font-size:12px}
.fxmenu .fxrow button{font-size:11px;white-space:nowrap}
.fxmenu .fxall{display:block;padding:6px 13px;font-size:12px}
.fxmenu .fxempty{padding:9px 13px;color:var(--slate);font-size:12px}
.fxmenu .fxsend{font-weight:600}
.fxmenu input[type=checkbox]{width:auto;height:auto;margin:0 5px 0 0;flex:none;display:inline-block}
.fxmenu .fxok,.fxmenu .fxall{display:inline-flex;align-items:center}
.fxmenu .fxall{display:flex}
#fxBtn .fbdot{background:rgba(27,25,23,.14)}
/* PLACE ONE, THEN OFFER ONE MORE (report #33, rail.js showAddAnother): the
   small box beside what was just placed. An offer, not a mode. */
#addAnother{position:absolute;z-index:9;display:flex;align-items:center;gap:2px;
  background:#fff;border:1px solid var(--ksp-selection);border-radius:8px;padding:3px;
  box-shadow:0 6px 20px rgba(20,26,34,.22);font-size:12.5px;white-space:nowrap}
#addAnother button{border:0;background:none;cursor:pointer;font:inherit;padding:4px 8px;
  border-radius:6px;color:var(--ksp-selection)}
#addAnother .aaAdd{font-weight:600}
#addAnother button:hover{background:rgba(20,26,34,.07)}
#addAnother .aaX{font-size:15px;line-height:1;padding:3px 7px;color:#555}
@media (pointer:coarse){#addAnother button{min-height:40px;min-width:40px}}

/* Report boxes coloured by state (Nick, 8 Oct; fbColour in web/js/feedback.js):
   RED open and not sent, YELLOW with a minion (up to "fixed, waiting to go
   live"), GREEN live on production, GREY cleared. A strip down the left edge
   and the state words in the same colour; the text stays dark on light. */
.fbrow.c-red{border-left-color:#c62828}
.fbrow.c-yellow{border-left-color:#e0a800}
.fbrow.c-green{border-left-color:#2e7d32}
.fbrow.c-grey{border-left-color:#9aa3ad}
.fbstate{display:inline-block;border-radius:3px;padding:0 5px;margin-right:6px;font:600 11px var(--mono)}
.fbc-red,.fbstage.fbc-red{background:#fdecea;color:#b71c1c}
.fbc-yellow,.fbstage.fbc-yellow{background:#fff4cc;color:#6b4f00}
.fbc-green,.fbstage.fbc-green{background:#e6f4ea;color:#1b5e20}
.fbc-grey,.fbstage.fbc-grey{background:#eef1f4;color:#5f6872}
.fbtabs button[data-tab="open"]{color:#b71c1c}
.fbtabs button[data-tab="sent"]{color:#8a6500}
.fbtabs button[data-tab="fixed"]{color:#1b5e20}
.fbtabs button[data-tab="archived"]{color:#5f6872}
.fbtabs button[data-tab="open"].on{border-bottom:3px solid #c62828}
.fbtabs button[data-tab="sent"].on{border-bottom:3px solid #e0a800}
.fbtabs button[data-tab="fixed"].on{border-bottom:3px solid #2e7d32}
.fbtabs button[data-tab="archived"].on{border-bottom:3px solid #9aa3ad}
/* Send selected: a tick box on every row and one bar over the list. */
.fbrow.selectable{position:relative}
.fbrow.selectable>button.fbhead{padding-left:34px}
.fbsel{position:absolute;left:8px;top:10px;z-index:1;cursor:pointer}
.fbsel input{width:16px;height:16px;margin:0;cursor:pointer}
.fbsel input:disabled{cursor:not-allowed}
.fbbatch{display:flex;flex-wrap:wrap;align-items:center;gap:6px;margin:4px 10px 8px;
  padding:6px 8px;background:#f4f6f8;border:1px solid var(--soft);border-radius:5px;font:12px var(--body)}
.fbbatch button{font:600 12px var(--body);padding:4px 8px;border-radius:4px;border:1px solid #c9d0d7;
  background:#fff;cursor:pointer}
.fbbatch button.pri{background:#7B1420;border-color:#7B1420;color:#fff;margin-left:auto}
.fbbatch button:disabled{opacity:.5;cursor:default}
.fbselcount{font:11px var(--mono);color:#4a545e}
.fburgent{display:inline-flex;align-items:center;gap:3px;color:#b71c1c;font-weight:600}

/* PHONE WIDTH (job 1009-0622-s10, rule q700/q612). Below 600px the three
   columns (60 rail + plan + 360 panel) left the plan 0px wide and the toolbar
   stacked down the left edge, so nothing could be touched. Here the panel is a
   bottom sheet under the plan (its own Hide panel folds it to a strip), the rail
   is 44px, the header and toolbar are one scrolling row each. CSS only: every
   control keeps its element and handler. Tablet and up are untouched. */
@media (max-width:600px){
  /* The order's line header: name, quantity and the Price level / Multiplier
     boxes do not fit one row at 390px and the boxes ran off the sheet. */
  .ogrp{flex-wrap:wrap}
  .ogrp .gctl{flex:1 0 100%;flex-wrap:wrap;margin-left:0}
  /* 3D camera chips: one row that scrolls sideways (they took three rows over
     the view); the cabinet-count pill beside it is compact, not a big floor pill */
  #cambar{flex-wrap:nowrap;overflow-x:auto;overflow-y:hidden;right:10px;max-width:none;
    -webkit-overflow-scrolling:touch;scrollbar-width:none;touch-action:pan-x}
  #cambar::-webkit-scrollbar{display:none}
  #cambar .cbtn,#cambar .cbsep{flex:0 0 auto}
  #cambar:not([hidden]) ~ #cabpill{padding:4px 9px;gap:5px;font-size:11px}
  #cambar:not([hidden]) ~ #cabpill .cphandle{display:none}
  #cambar:not([hidden]) ~ #cabpill .cpbtn{width:16px;height:16px;margin-left:0}
  header{display:flex;gap:6px;padding:4px 8px;overflow-x:auto;overflow-y:hidden;border-bottom-width:3px}
  header .hleft,header .hright,header .hcentre{flex:0 0 auto;display:flex;align-items:center;gap:6px}
  header .hcentre,#verstamp,#userbtn .whoname,.whocaret{display:none}
  header .hlbl{display:none}
  header button{min-height:36px;min-width:36px}
  #mode{flex:0 0 auto}
  #mode #orderTab{padding:7px 12px;min-height:36px}
  .roombar{padding:4px 8px;overflow-x:auto;overflow-y:hidden}
  .rbtools{flex-wrap:nowrap;flex:0 0 auto;min-width:max-content}
  .rbtools .vtools,.rbtools .seg2{flex:0 0 auto;flex-wrap:nowrap}
  .rbtools .s,.rbtools button,.rbtools .seg2 button{min-height:36px;white-space:nowrap}
  main{grid-template-columns:44px minmax(0,1fr);grid-template-rows:minmax(0,1fr) minmax(0,44vh)}
  main.wide{grid-template-columns:44px minmax(0,1fr);grid-template-rows:minmax(0,1fr) 40px}
  #sideL{grid-column:1 / -1;grid-row:2;border-left:0;border-top:2px solid var(--line)}
  main.wide #sideL{border-top:0}
  main.wide #ptabs{flex:1;padding:0;border:0;background:transparent}
  main.wide #ptabs .ptoggle{flex:1 1 auto;width:auto;min-width:0;height:40px;align-self:stretch}
  main.wide #ptabs .ptoggle .ptlab{display:inline}
  #ptabs .ptoggle{flex:0 0 auto;min-width:44px;min-height:40px}
  .rail button{width:40px}
  .rail #railCabinet{width:44px;min-height:56px}
  .rail .rlab{font-size:8px}
  .rail button{min-height:44px}
  header .hleft{container-type:normal}
  header .hleft,header .hright{min-width:max-content;overflow:visible}
  header .soon::after{display:none}
  header .soon{padding-right:8px}
  #hacts{flex:0 0 auto;overflow:visible}
  .rbtools{padding-right:0}
  .roombar .rbroom{position:static;flex:0 0 auto;width:auto}
  .drawbar{left:6px;right:6px;transform:none;bottom:calc(44vh + 8px);flex-wrap:wrap;justify-content:center;padding:8px 10px;border-radius:14px}
  .drawbar .dhint{display:none}
  .drawbar .dbtn{min-height:40px}
  #sidegapbox{flex-wrap:wrap;gap:6px 14px;justify-content:center;max-width:calc(100% - 16px)}
  #stage{overflow:hidden}
  /* the cabinet browser: a title row, then the search and the scope on one row */
  .cbhead{flex-wrap:wrap;gap:8px;padding:8px 10px}
  .cbhead h2{flex:1 1 auto}
  .cbhead #cbSearch{order:5;flex:1 1 100%;height:38px}
  .cbhead .cbscope{order:6;flex:1 1 100%;max-width:none;height:38px}
  .cbhead .cbico{min-height:38px;min-width:38px}
  .cbchip,.cbchips button{min-height:34px}
  /* the group row and the kinds row (job 1011-2042): tighter, so they leave the cards in reach */
  #cbGroups,#cbTypes{padding:6px 10px;gap:5px}
  /* the stage is ~250px tall: the whole browser scrolls, so the cards under the chip rows are reachable */
  #cabBrowser .cbmain{overflow-y:auto;overscroll-behavior:contain}
  #cabBrowser .cbgrid{flex:none;overflow:visible}
  #cabBrowser .cbhead{position:sticky;top:0;z-index:2}
  #cabpill .cpbtn,#cabpillMax{min-height:36px;min-width:36px}
  /* a window / door / appliance sheet sits under the top of the drawing, so the wall
     you are about to tap is not behind it (job 1010-2050, report item 4) */
  #cabBrowser[data-tool]:not([data-tool="cabinet"]):not(.collapsed){top:auto;left:0;width:100%;height:auto;
    max-height:max(50%,204px);border-left:0;border-top:1px solid var(--line);box-shadow:0 -8px 22px rgba(20,26,34,.13)}
  /* the stage is ~290px at 390x844: a square-drawing card (216px) never fitted under the head and the size
     boxes, so the first tap landed on the dock's chip. In the sheet a card is a row (drawing left, words
     right) ~88px tall: the first row is whole without a scroll (job 1010-2246-s9-000). */
  #cabBrowser[data-tool]:not([data-tool="cabinet"]) .cbgrid{grid-template-columns:repeat(auto-fill,minmax(150px,1fr));padding:8px 10px}
  #cabBrowser[data-tool]:not([data-tool="cabinet"]) .cbcard{display:grid;grid-template-columns:64px minmax(0,1fr);
    grid-auto-rows:min-content;align-content:center;column-gap:8px;row-gap:2px;padding:6px}
  #cabBrowser[data-tool]:not([data-tool="cabinet"]) .cbcard .cbdraw{grid-column:1;grid-row:1/span 3;width:64px}
  #cabBrowser[data-tool]:not([data-tool="cabinet"]) .cbcard>:not(.cbdraw){grid-column:2}
  #cabBrowser[data-tool]:not([data-tool="cabinet"]) .cbcard{overflow:hidden}
  #cabBrowser[data-tool]:not([data-tool="cabinet"]) .cbcard .cbdesc{display:-webkit-box;-webkit-box-orient:vertical;
    -webkit-line-clamp:2;line-clamp:2;overflow:hidden}
  #cabBrowser[data-tool]:not([data-tool="cabinet"]) .cbcard>*:not(.cbdraw){min-width:0;overflow-wrap:anywhere}
  /* a handful of cards: the search and the chip rows are what filled the sheet */
  #cabBrowser[data-tool]:not([data-tool="cabinet"]) #cbSearch,
  #cabBrowser[data-tool]:not([data-tool="cabinet"]) #cbGroups,
  #cabBrowser[data-tool]:not([data-tool="cabinet"]) #cbTypes,
  #cabBrowser[data-tool]:not([data-tool="cabinet"]) #cbWidths,
  #cabBrowser[data-tool]:not([data-tool="cabinet"]) .pknote{display:none}
  /* the toolbar row comes to rest on a button's edge, never half of one ("ected") */
  .roombar button,.roombar select,.roombar label{scroll-snap-align:start}
  .roombar{scroll-snap-type:x proximity;scroll-padding-left:8px}
}
/* The same sheet on a touch tablet (browser.js cbUseSheet decides, by tool and width): the room is fitted to
   the band above it, so every wall is there to tap (job 1010-2215-s8-05). */
#cabBrowser.cbsheet:not(.collapsed){top:auto;right:0;left:0;bottom:0;width:100%;height:50%;
  border-left:0;border-top:1px solid var(--line);box-shadow:0 -8px 22px rgba(20,26,34,.13)}
/* A FOLDED BROWSER ON A TOUCH SCREEN carries its own 44px X: the header's close is folded away with
   the cards, and the only way out was a tap on a 34px strip nothing explained (job 1010-2050). */
@media (max-width:600px),(pointer:coarse){
  #cabBrowser.collapsed,#cabBrowser.flyout.collapsed{width:45px!important}
  #cabBrowser.collapsed .cbstrip{padding-top:48px}
  #cabBrowser.collapsed .cbstripx{display:flex;position:absolute;top:0;left:0;width:44px;height:44px;
    align-items:center;justify-content:center;border:0;border-bottom:1px solid var(--line);
    background:var(--card);color:var(--ink);font-size:18px;cursor:pointer;z-index:3}
}

/* THE PHONE DOCK (job 1010-2058; startup.js moves the gap card and the chip in).
   Up to 900px - a phone and a portrait tablet, where the stage is under 500px wide
   and the two floating boxes overlapped each other and the plan's bottom row. */
@media (max-width:900px){
  /* the dock: the gap card and the count chip sit in a row under the plan, not on it (job 1010-2058) */
  #phoneDock{display:flex;flex-direction:column;align-items:center;gap:6px;flex:0 0 auto;
    background:var(--paper);border-top:1px solid var(--line)}
  #phoneDock:not(:has(>:not([hidden]))){display:none}
  #phoneDock #sidegapbox,#phoneDock #cabpill{position:static!important;transform:none;margin:4px 0 0;
    left:auto!important;right:auto!important;top:auto!important;bottom:auto!important;box-shadow:none}
  #phoneDock #cabpill:last-child{margin-bottom:4px;padding-top:5px;padding-bottom:5px}
  #phoneDock #sidegapbox{flex-direction:row;flex-wrap:nowrap;gap:4px 10px;padding:4px 6px;max-width:calc(100% - 12px)}
  #phoneDock #sidegapbox .sgrow{margin:0}
  #phoneDock #sidegapbox .sgto{display:none}
  #phoneDock #sidegapbox .cmpos{padding:2px 2px;gap:3px}
  #phoneDock #sidegapbox .cmpos label{width:34px}
  #phoneDock #sidegapbox .cmpos input{width:46px}
  #phoneDock #cabpill .cphandle{display:none}
  /* one row under the plan, not two: while the gap card is up the chip waits (deselect and it is back) */
  #phoneDock:has(#sidegapbox:not([hidden])) #cabpill{display:none}
}

/* ORDER SHEET AND PRINT PREVIEW ON A PHONE (job 1010-2053-s8-05). Screen only:
   the printed order and the printed plan are not touched.
   The order table's one-line rows ellipsised the item to "BAS..." once the
   SKU, Qty, Each and Price columns had their fixed widths. On a phone the item
   and the SKU wrap instead, and the money columns take less. The preview's
   sheet was squeezed to the screen width (~330px for a Letter-landscape
   page); it keeps a readable width and the sheet scrolls, or pinches. */
@media screen and (max-width:600px){
  .ordtbl th,.ordtbl td{white-space:normal;overflow:visible;text-overflow:clip;
    overflow-wrap:break-word;vertical-align:top}
  .ordtbl td.osku{width:23%;font-size:10px;overflow-wrap:anywhere}
  .ordtbl th.n,.ordtbl td.oqty{width:22px}
  .ordtbl th.oeach,.ordtbl td.oeach{width:56px}
  .ordtbl th:last-child,.ordtbl td.oprice{width:60px}
  .ordtbl td.oeach,.ordtbl td.oprice{font-size:11px}
  .ordtbl th,.ordtbl th.oeach,.ordtbl th:last-child{letter-spacing:0;font-size:8.5px}
  .ordtbl td.oitem{font-size:11.5px}
  .ppmask{padding:8px}
  .ppbox{max-height:100%}
  .ppbar{flex-wrap:wrap;gap:8px}
  .ppbar .pphint{flex:1 1 100%;order:3}
  .ppsheet{touch-action:pan-x pan-y pinch-zoom;-webkit-overflow-scrolling:touch}
  .ppsheet .ppsvg{width:900px;max-width:none}
  .ppsheet .pppage{width:max-content}
}

/* ORDER SHEET FITS A PHONE (job 1010-2240-s8-000). At 390x844 with the names
   wrapping, the lines table got ~90px between the head block and the pinned
   foot. Three causes, all layout: (1) every .pricectl row kept its grey hint
   beside the control, which squeezed "Price level - set all lines" to a 12px
   sliver; (2) the window kept 24px side margins on a 390px screen; (3) the
   foot spent two button rows and two total rows. Here the hints go (the
   control's title carries them), the long Price level label sits above a
   full-width select, the window is nearly full-bleed, and the foot's two
   totals share one row. Screen and phone only; print and desktop untouched. */
@media screen and (max-width:600px){
  #po{left:6px;right:6px;bottom:6px;padding:8px 10px 10px}
  #po .ordhd{padding:8px 4px 6px}
  .ordbody{padding:8px 4px 10px}
  .ordbody .pricectl{flex-wrap:wrap;row-gap:3px}
  .ordbody .pricectl .hint{display:none}
  .ordbody .custctl{margin:0 0 6px}
  /* job 1018: the Summary and countertop editor sit below the lines on a phone, so the lines keep the room */
  .ordbody{display:flex;flex-direction:column}
  .ordbody>*{order:0;flex:0 0 auto}
  .ordbody>.ordsum,.ordbody>.ordsumedit{order:5}
  .ordbody .pricectl select.ordlevel{flex:1 0 100%}
  .ordfoot{padding:6px 4px 8px}
  .ogrp{flex-wrap:wrap}
  .ogrp .gctl{flex:1 0 100%;margin-left:0;gap:8px}
  .ogrp .gctl select{flex:1 1 auto;min-width:0}
  .ordbtns{margin-top:6px;gap:6px}
  .ordwarn,.ordok{margin:0 0 8px;padding:6px 8px;line-height:1.35}
  .ordwarn ul{margin:3px 0 4px 14px}
  .ordsale{margin:0 0 6px;padding:5px 8px}
  .ogrp{margin:6px 0 2px}
  .ordbtns{grid-template-columns:repeat(3,1fr)}
  .ordbtns button{padding:6px 4px;font-size:11.5px;line-height:1.2;min-height:44px}
  .ordbilllist{min-width:210px}
}

/* ORDER SHEET NUMBERS NEVER WRAP (job 1010-2254-s8-000). The money columns were
   56px and 60px, so "$1,928.00" broke to "$1,928.0" over "0" and "$11,788.00"
   the same. Qty, Each and Price (cells and heads) stay on one line and get the
   width of the widest real figure at 11px; the Item column takes the slack and
   keeps wrapping. Screen and phone only. */
@media screen and (max-width:600px){
  .ordtbl td.oqty,.ordtbl td.oeach,.ordtbl td.oprice,
  .ordtbl th.n,.ordtbl th.oeach,.ordtbl th:last-child{white-space:nowrap;overflow-wrap:normal}
  .ordtbl th.n,.ordtbl td.oqty{width:24px}
  .ordtbl th.oeach,.ordtbl td.oeach{width:68px}
  .ordtbl th:last-child,.ordtbl td.oprice{width:70px}
  .ordtbl td.osku{width:20%}
}

/* PRINT PREVIEW BUTTONS ARE FINGER-SIZED (job 1010-2240-s8-000): Print... and
   Close were 26px tall at 390 and 820. Coarse pointers only; a mouse keeps the
   compact bar. Same 44px as the other phone controls (q889's X). */
@media (pointer:coarse){
  .ppbar button{min-height:44px;min-width:44px}
  .pricectl select,.pricectl input,.ogrp .gctl select,.ogrp .gctl input{min-height:36px}
}

/* TOUCH TARGETS (job 1009-0622-s10): on a touch screen the toolbar's 20-25px
   buttons and the panel's 28px tabs were too small to hit. Coarse pointers only,
   so a mouse keeps the compact toolbar. */
@media (pointer:coarse){
  .rbtools button,.rbtools select,.seg2 button,header button{min-height:36px}
  #ptabs .ptab{min-height:40px}
}

/* The 3D control in the view switcher (.seg2) is ONE split segment: the label
   (#v3d, "3D · <mode>") and its arrow (#v3darrow) light and hover together.
   The label has a fixed minimum width so Perspective and Top, and 2D, 3D and
   Elevation, never move the buttons beside it. */
.seg2 .v3dsplit{display:flex}
.seg2 .v3dsplit button+button{border-left:1px solid rgba(0,0,0,.12)}
.seg2 .v3dsplit button.on+button.on{border-left-color:rgba(255,255,255,.25)}
#v3d{min-width:15ch;text-align:center}
#v3darrow{padding-left:5px;padding-right:6px}
.seg2 .v3dsplit:hover button:not(.on){background:var(--sage);color:#fff}
.seg2 #v3darrow:focus-visible,.seg2 #v3d:focus-visible{outline:2px solid var(--sage);outline-offset:-2px}
.roommenu .rmitem:focus-visible,.roommenu .rmitem.kb{background:#eef2f6;outline:none}
/* the lights tool's panel (job 1011-2119) */
.lt-tools{display:flex;flex-direction:column;gap:6px;margin-top:8px}
.lt-group{font-size:11px;text-transform:uppercase;letter-spacing:.04em;opacity:.65;margin-top:4px}
.lt-style{display:block;width:100%;text-align:left;padding:6px 8px;border-radius:6px;border:1px solid currentColor;background:transparent;color:inherit;cursor:pointer;opacity:.85}
.lt-style.lt-sel{opacity:1;border-width:2px}
.lt-row{display:flex;align-items:center;gap:6px}
.lt-count{width:auto;font-size:12px;opacity:.75;white-space:normal}
.lt-help{font-size:12px;opacity:.7}
.ordlighting ul{margin:4px 0 0 18px;padding:0}
/* My colours (the designer's saved wall colours, job 1013-1102-s10) */
.mycolours{margin:4px 0 8px}
.mcrow{display:flex;align-items:center;gap:3px;margin-bottom:3px}
.mcrow.on .mcuse{outline:2px solid var(--ink)}
.mcuse{display:flex;align-items:center;gap:6px;flex:1;min-width:0;padding:3px 4px;border:1px solid var(--line);border-radius:4px;background:transparent;cursor:pointer;font-size:11px;text-align:left}
.mcuse b,.mcuse i{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.mcuse i{color:var(--slate);font-style:normal}
.mcedit,.mcdel{border:1px solid var(--line);border-radius:4px;background:transparent;cursor:pointer;padding:2px 6px;font-size:12px}
.mcform{display:grid;gap:3px;margin:4px 0}
.mcform input{width:100%}
.mcbtns{display:flex;gap:4px}
/* trim runs under their SKU line on the Order (job 1014-2146-s8) */
.ordtbl tr.ordtrim td{border-top:0;padding-top:0;font-size:11.5px;color:var(--slate)}
/* Phone: the customer section is one find row, so the order lines keep the sheet (job 1017 harness) */
@media (max-width:600px){
  .ordcustsec{margin:0 0 4px}
  .ordcustsec>b,.ordcustsec>.dsc{display:none}
  .ordcustfind{margin-top:0}
}

/* The view switcher (2D Plan, 3D, Elevation) is three SEPARATE buttons, spaced
   and outlined like ⚙ Render and 🌙 Night (Nick, 17 Oct 2026), not one joined
   strip. The wrapper keeps the .seg2 class so mountRenderButton still finds
   it; only its look changes. The active view keeps the .on colour above and
   only one is on at a time. The 3D label and its arrow stay one button: the
   outline is on .v3dsplit, not on each half. */
.rbtools .seg2.vbtns{border:0;border-radius:0;overflow:visible;gap:5px;align-items:center}
.rbtools .seg2.vbtns button,.rbtools .seg2.vbtns .v3dsplit{
  border:1px solid var(--ink);border-radius:3px;background:transparent;color:var(--ink)}
.rbtools .seg2.vbtns button+button{border-left:1px solid var(--ink)}
.rbtools .seg2.vbtns .v3dsplit{overflow:hidden}
.rbtools .seg2.vbtns .v3dsplit button{border:0;border-radius:0;background:transparent}
.rbtools .seg2.vbtns .v3dsplit button+button{border-left:1px solid rgba(0,0,0,.12)}
.rbtools .seg2.vbtns button{font-size:11px;font-weight:600;padding:4px 10px}
.rbtools .seg2.vbtns .v3dsplit button#v3darrow{padding-left:5px;padding-right:6px}
.rbtools .seg2.vbtns button:hover:not(.on),.rbtools .seg2.vbtns .v3dsplit:hover button:not(.on){
  background:var(--ink);color:var(--paper)}
.rbtools .seg2.vbtns button.on{background:#3a3f45;color:#fff;border-color:#3a3f45}
.rbtools .seg2.vbtns .v3dsplit:has(button.on){border-color:#3a3f45}
.rbtools .seg2.vbtns .v3dsplit button.on+button.on{border-left-color:rgba(255,255,255,.25)}

/* FIT AND ZOOM, floating in the grid's top-right corner (18 Oct 2026). Inside
   #stage, so it is clear of the right panel; the cabinet legend sits below it
   (top:52px). When the cabinet browser slides in from the right it moves out
   of its way exactly as the legend does. */
#gridzoom{position:absolute;top:10px;right:10px;z-index:6;display:flex;gap:4px;
  align-items:center;padding:3px;background:rgba(255,255,255,.92);
  border:1px solid var(--t3-line);border-radius:9px;box-shadow:0 1px 4px rgba(0,0,0,.12)}
#gridzoom button[hidden]{display:none}
#stage.cbopen:not(.cbfly) #gridzoom{right:calc(min(820px,80%) + 12px)}
#stage.cbopen.cbdrag:not(.cbfly) #gridzoom{right:46px}

/* clean order look */
/* Order screen and Order PDF (job 1018-0059-s8, Nick 18 Oct): white page,
   larger bold dark column headings, darker rules under the heading row,
   between sections and above the totals, more air, fewer grey tints. Colour
   and spacing only; no content moves. Last in the file so it wins. */
#po,#ordCopy{background:#fff}
#po{padding:20px 32px 32px}
.ordtbl th{font-size:14px;font-weight:700;letter-spacing:.02em;text-transform:none;
  color:#1b232d;padding:10px 4px 10px 0;border-bottom:2px solid #3a4452}
.ordtbl th.n,.ordtbl th.oeach{text-align:right}
.ordtbl td{padding:9px 4px 9px 0;border-bottom:1px solid #e6e9ed}
.ordtbl tr.opg td{padding:14px 0 4px;color:#1b232d;border-bottom:0}
.ordtbl td.oitem{color:#1b232d}
.ogrp{margin:26px 0 8px;padding:10px 0 8px;background:#fff;border-left:0;border-radius:0;
  border-top:2px solid #3a4452}
.ogrp .nm{font-size:13.5px;color:#1b232d}
.ogrp .qt{color:#4a5563}
.ogrp .st{font-size:13.5px}
.ordfoot{border-top:2px solid #3a4452;margin-top:22px;padding-top:14px;background:#fff}
.ordtot{padding:5px 0}
.ordsale{background:#fff;border-top:1px solid #c9ced6}
.ordtbl td.oqty,.ordtbl td.oeach{overflow:visible}
.oledit{width:100%;box-sizing:border-box;text-align:right;font:inherit;
  font-variant-numeric:tabular-nums;padding:3px 6px;border:1px solid #9aa3af;
  border-radius:4px;background:#fff;color:#1b232d}
.oledit:focus{outline:2px solid #3a4452;outline-offset:0}
.ordtbl th.n,.ordtbl td.oqty{width:92px}
.ordtbl th.oeach,.ordtbl td.oeach{width:118px}
.ordtbl th:last-child,.ordtbl td.oprice{width:84px}
.ordtbl td.oqty .oledit{width:52px}
.ordtbl td.oeach .oledit{width:70px}
.oedited{font-size:11px;color:#8a5a00;margin-top:2px;white-space:nowrap}
.oreset{font:inherit;font-size:11px;padding:0;border:0;background:none;color:#1d4f9f;
  text-decoration:underline;cursor:pointer}
.ordsend.ordsoon{opacity:.75;cursor:not-allowed}
.ordsoon .soontag{display:inline-block;margin-left:8px;padding:1px 5px;border-radius:3px;
  font:800 8px/1.3 var(--sans,inherit);letter-spacing:.5px;text-transform:uppercase;
  color:#fff;background:rgba(183,48,48,.9);vertical-align:1px}
@media screen and (max-width:600px){
  #po{padding:10px 8px 16px}
  .ordtbl th{padding:4px 4px 4px 0}
  .ordtbl td{padding:4px 4px 4px 0}
  .ordtbl tr.opg td{padding:5px 0 1px}
  .ogrp{margin:4px 0 2px;padding:4px 0 3px}
  .ogrp .nm,.ogrp .st{font-size:12px}
  .ordsale{margin:0 0 6px;padding:4px 8px}
  .ordfoot{margin-top:12px;padding-top:8px}
  .ordsum{margin:4px 0 6px;padding:4px 8px}
  .ordsumedit{margin:0 0 6px;flex-wrap:wrap;row-gap:4px}
  .ordsumedit label{flex:0 0 auto}
  .ordsumedit select{flex:1 1 0;min-width:0}
  .ordsumedit input{flex:1 1 0;min-width:0}
  .ordsumedit button{flex:0 0 auto}
  .ordsumttl{display:none}
  .ordbody{padding:4px 4px 10px}
  .ordbody .custctl{margin:0 0 3px}
  .ordwarn,.ordok{margin:0 0 4px}
  .ordbody .pricectl{row-gap:2px}
  .ordtbl th:first-child,.ordtbl td.osku{width:56px}
  .ordtbl th.n,.ordtbl td.oqty{width:26px}
  .ordtbl th.oeach,.ordtbl td.oeach{width:67px}
  .ordtbl th:last-child,.ordtbl td.oprice{width:70px}
  .ordtbl td.oeach,.ordtbl td.oprice{font-size:11px}
  .ordtbl th,.ordtbl th.oeach,.ordtbl th:last-child{font-size:9px;letter-spacing:0}
  .ordtbl td.oqty .oledit,.ordtbl td.oeach .oledit{width:100%;padding:3px 2px}
  /* the lines come first on a phone: Summary and the countertop editor follow the notes */
  .ordbody{display:flex;flex-direction:column}
  .ordbody>*{flex:0 0 auto}
  .ordbody>.ordsum,.ordbody>.ordsumedit{order:1}
}
@media print{
  #ordCopy,#ordCopy .ordtbl{background:#fff}
  .ordtbl th{border-bottom:2px solid #3a4452;color:#1b232d}
}
/* end clean order look */

/* Who's online: a company with more people online than licences (1019) */
.constable tr.licover td{background:#fbe9e7;color:#8d2020}

/* THE RAIL'S BOXES (Counters, Floor): a small panel beside the rail, placed
   by railBoxToggle() in rail.js. */
.railbox{position:fixed;z-index:40;width:250px;padding:10px 12px;background:#fff;
  border:1px solid #cfd6e0;border-radius:8px;box-shadow:0 6px 20px rgba(0,0,0,.2)}
.railbox[hidden]{display:none}
.railboxhd{display:flex;align-items:center;justify-content:space-between;margin-bottom:8px;
  font-size:12px;font-weight:600;color:var(--ink)}
.railboxx{display:flex;align-items:center;justify-content:center;width:24px;height:24px;padding:0;
  border:0;border-radius:4px;background:transparent;color:var(--slate);font-size:18px;line-height:1;cursor:pointer}
.railboxx:hover{background:var(--t3-on);color:var(--ink)}
.railboxline{height:1px;margin:8px 0;background:#e3e8ef}
.railbox select.cmini{flex:1 1 120px;min-width:0}

/* Mouldings & trim (job 1017): label | setting | fixed-width button column. */
.mouldbox{margin:10px 0;padding:8px 10px;border:1px solid var(--line,#d8d8d8);border-radius:8px}
.mouldhd{font-size:12px;font-weight:600;margin-bottom:6px}
.mouldrow{display:grid;grid-template-columns:78px minmax(0,1fr) 96px;align-items:center;column-gap:8px;margin:5px 0}
.mouldrow[hidden]{display:none}
.mouldrow .mlab{font-size:11px;font-variant:small-caps;color:#8a8a8a;letter-spacing:.03em}
.mouldrow .mset{display:flex;gap:6px;align-items:center;min-width:0}
.mouldrow .mset .s{font-size:11px;line-height:1.3;color:#6f6f6f}
.mouldrow .mset select{flex:1 1 auto;min-width:0}
.mouldrow .mset input{width:46px}
.mouldrow .mbtn{display:flex;gap:6px;justify-content:flex-end;align-items:center}
.mouldrow .mbtn button{cursor:pointer}
#camlock[hidden]{display:none!important}

/* THE STANDARD WINDOWS (job 1022): Mouldings & trim, Countertops, Flooring. The
   Cabinets browser's frame (title bar with X, chips, card grid) as a floating
   box beside the rail. railBoxToggle() in rail.js places it; Esc closes it. */
.railbox.stdwin{width:min(560px,calc(100vw - 96px));max-height:calc(100vh - 16px);display:flex;flex-direction:column;
  padding:0;border:1px solid var(--line);border-radius:10px;overflow:hidden;box-shadow:0 14px 44px rgba(20,26,34,.28)}
.railbox.stdwin[hidden]{display:none}
.stdwin .railboxhd.cbhead{margin:0;font-size:inherit;cursor:grab;user-select:none;touch-action:none}
.stdwin .railboxhd.cbhead button{cursor:pointer}
.stdwin .railboxx.cbico{width:30px;height:30px;font-size:18px;color:var(--slate)}
.stdwin .stdtabs{padding:8px 14px 0}
.stdwin .stdgrid{flex:0 1 auto;max-height:46vh;grid-template-columns:repeat(auto-fill,minmax(112px,1fr))}
.stdwin .stdgrid:empty{display:none}
.stdwin .stdbody{padding:8px 14px 12px;overflow-y:auto;min-height:0}
.stdwin .stdbody:empty{display:none}
/* THE SURFACES WINDOW (job 1022): Countertops / Flooring buttons side by side; the pressed one is filled. */
.stdwin .surfsides{display:grid;grid-template-columns:1fr 1fr;gap:8px;padding:10px 14px 0}
.stdwin .surfsides button{padding:8px 0;border:1px solid var(--ksp-selection);border-radius:6px;background:#fff;
  color:var(--ksp-selection);font:inherit;font-weight:600;cursor:pointer}
.stdwin .surfsides button.on{background:var(--ksp-selection);color:#fff}
.stdwin .surfpane[hidden]{display:none}
.stdwin .surfpane{display:flex;flex-direction:column;min-height:0;overflow-y:auto}
/* THE COUNTERTOPS WINDOW (job 1022): four numbered sections, label left. */
.cwin .cwsec{display:grid;grid-template-columns:104px minmax(0,1fr);column-gap:12px;padding:10px 0;align-items:start}
.cwin .cwsec+.cwsec{border-top:1px solid var(--line)}
.cwin .cwlab{font-size:11px;font-weight:600;color:var(--slate);text-transform:uppercase;letter-spacing:.03em;line-height:1.3}
.cwin .cwlab b{display:inline-block;width:18px;height:18px;margin-right:6px;border-radius:9px;background:var(--ksp-selection);
  color:#fff;font-size:11px;line-height:18px;text-align:center;letter-spacing:0}
.cwin .cwset{min-width:0}
.cwin .cwset .cbchips{padding:0 0 8px;border-bottom:0}
.cwin .cwset .cbgrid{padding:0;max-height:34vh;grid-template-columns:repeat(auto-fill,minmax(92px,1fr))}
.cwin .cwset .cbgrid .surfcard.on{outline:2px solid var(--ksp-selection);outline-offset:1px}
.cwin .cwrow{display:flex;align-items:center;gap:8px}
.cwin .cwrow .cwtxt{flex:1;font-size:12px}
.cwin .cwhint{margin-top:3px;font-size:11px;line-height:1.3;color:#6f6f6f}
.cwin .cwtwo{display:grid;grid-template-columns:1fr 1fr;gap:8px}
.cwin .cwswitch{flex:none;min-width:52px;text-align:center;cursor:pointer;font-weight:600}
.cwin .cwswitch:not(.g){background:var(--ksp-selection);border-color:var(--ksp-selection);color:#fff}
@media (max-width:520px){.cwin .cwsec{grid-template-columns:1fr;row-gap:6px}}
#railBoxMould{width:min(480px,calc(100vw - 96px))}
#railBoxMould .mouldbox{margin:0;border:0;padding:10px 14px 14px;overflow-y:auto}
.surfcard{text-align:left;cursor:pointer;font:inherit;gap:4px}
.surfcard .surfsw{display:block;width:100%;aspect-ratio:3/2;border-radius:6px;border:1px solid rgba(0,0,0,.2);
  background-size:cover;background-position:center}
/* The Surfaces pull-out: two buttons that slide out beside the rail button. */
.railslide{position:fixed;z-index:41;display:flex;gap:4px;padding:3px;background:#fff;border:1px solid #cfd6e0;
  border-radius:8px;box-shadow:0 6px 20px rgba(0,0,0,.2);flex-direction:column;width:72px;
  opacity:0;transform:translateX(-14px);transition:transform .16s ease,opacity .16s ease}
.railslide.out{opacity:1;transform:none}
.railslide[hidden]{display:none}
.railslide button{display:flex;flex-direction:column;align-items:center;gap:3px;width:100%;padding:7px 0 6px;
  border:1px solid transparent;border-radius:6px;background:transparent;color:var(--t3-ink);font-weight:500;cursor:pointer}
.railslide button .ti{font-size:19px;line-height:1}
.railslide .rlab{font-size:9px;line-height:1;white-space:nowrap}
.railslide button:hover{background:var(--t3-on);color:var(--ink)}
.railslide button.on{background:color-mix(in srgb, var(--accent) 14%, transparent);color:var(--accent);border-color:color-mix(in srgb, var(--accent) 35%, transparent)}
@media (prefers-reduced-motion:reduce){.railslide{transition:none}}

/* ---- THE SELECTED SECTION of the right panel (job 1022) --------------------
   What a click or right-click on an item used to float over the drawing - name,
   Left / Right box, actions, Set size - sits here, under the tab's content. */
#selsec{flex:0 1 auto;max-height:62%;overflow-y:auto;margin:6px 10px 10px;border:1px solid var(--line);border-radius:10px;background:#fbfaf7}
#selsec[hidden]{display:none}
#selsec .cabpop.cabmenu,#selsec .cabpop.cabmenu.fpmenu{position:static;width:auto;max-width:none;
  box-shadow:none;border:0;background:transparent;overflow:visible;z-index:auto}
#selsec #sidegapbox{position:static;transform:none;box-shadow:none;max-width:none;margin:4px 0;
  flex-wrap:wrap;background:#fff}
#selsec .selgap:empty{display:none}
.selsize{display:flex;flex-direction:column;gap:4px;padding:6px 8px;border-top:1px solid var(--soft)}
.selsize .selsizeh{font-weight:600;font-size:12px}
.selsize .cmat{display:flex;align-items:center;gap:6px}
.selsize .cmat label{width:54px}
.selsize .cmat input{width:70px}

/* THE ORDER IN TWO COLUMNS (job 1026; Nick approved the mockup 22 Oct 2026).
   LEFT: the lines, full height. RIGHT, narrow and staying put while the lines
   scroll: cards (Customer, Pricing, Summary, Warnings) over the pinned totals
   and buttons. Last in the file so it wins. Desktop first; the phone and paper
   rules follow. */
.ordcols{display:grid;grid-template-columns:minmax(0,1fr) 360px;gap:0 22px;
  flex:1;min-height:0}
.ordleft{min-height:0;overflow-y:auto;overflow-x:hidden;padding:4px 14px 14px 0}
.ordright{min-height:0;display:flex;flex-direction:column;border-left:1px solid #d5dae1;padding-left:18px}
.ordcards{flex:1;min-height:0;overflow-y:auto;overflow-x:hidden;padding:4px 4px 6px 0;scrollbar-gutter:stable}
.ordcard{border:1px solid #c9ced6;border-radius:8px;padding:8px 10px;margin:0 0 8px;background:#fff}
.ordcard .ordcustsec{margin:0}
.ordcardttl{font-weight:700;font-size:12px;text-transform:uppercase;letter-spacing:.04em;
  color:var(--slate);margin-bottom:4px}
.ordright .ordsum{margin:0 0 8px}
.ordright .ordcustfind{flex-wrap:wrap}
.ordright .ordfoot{margin-top:0;padding:10px 4px 0 0;border-top:2px solid #3a4452}
.ordright .ordbtns{grid-template-columns:1fr;gap:6px}
.ordright .ordbtns button{padding:8px 8px}
.ordwarnline{background:#fff4dc;border:1px solid #d99a1e;color:#5c3d05;border-radius:8px;
  padding:5px 10px;margin:0 0 8px;font-size:12px}
.ordwarnline .ordwarnhd{display:flex;align-items:center;justify-content:space-between;gap:8px}
.ordwarnline ul{margin:4px 0 6px 16px;padding:0}
.ordwarnline .lnk{background:none;border:0;padding:0;color:#7a4a00;text-decoration:underline;
  cursor:pointer;font:inherit;font-weight:600}
@media screen and (min-width:601px){
  #po{top:56px;bottom:16px;left:16px;right:16px;padding:14px 24px 16px;overflow:hidden}
}
@media screen and (max-width:600px){
  .ordcols{display:flex;flex-direction:column;overflow-y:auto}
  .ordleft{overflow:visible;flex:none;order:2;padding:0}
  .ordright,.ordcards{display:contents}
  .ordcard,.ordsum,.ordwarnline,.ordswitch{order:1}
  .ordright .ordsum{order:3}
  .ordright .ordfoot{order:9;position:sticky;bottom:0;background:#fff;padding:6px 4px 8px}
  .ordright .ordbtns{grid-template-columns:repeat(3,1fr)}
  /* the Pricing rows were inside .ordbody; the same phone rules, now for the card */
  .ordright .pricectl{flex-wrap:wrap;row-gap:3px}
  .ordright .pricectl .hint{display:none}
  .ordright .custctl{margin:0 0 6px}
  .ordright .pricectl select.ordlevel{flex:1 0 100%}
  .ordcardttl{display:none}
}
@media print{
  body.printing-order .ordcols{display:flex !important;flex-direction:column}
  body.printing-order .ordright,body.printing-order .ordcards{display:contents}
  body.printing-order .ordcard,body.printing-order .ordsum{order:1}
  body.printing-order .ordleft{order:2;overflow:visible !important;padding:0}
  body.printing-order .ordright .ordfoot{order:3;padding:14px 0 0}
  body.printing-order .ordwarnline,body.printing-order .ordcardttl{display:none !important}
}
