.adj-qty input{text-align:right;font-variant-numeric:tabular-nums;}
.adj-js .adj-save{display:none !important;}
.adj-saving .adj-qty input{border-color:#1d5b9e;}

/* Row coloring: full-row highlight when STATUS - VARIANCE is Out of Range */
.adj-row.adj-oor{background-color:#F3BBBB;}

/* Freeze pane: JS (layoutFreeze) sets position:fixed + left/width/top on .freeze-zone wrapper elements directly (position:sticky doesn't work here because Baserow wraps each element in a container sized exactly to its content, giving sticky no room to engage). This class only marks which elements to target; layout is inline via JS. */
.freeze-zone-spacer{display:block;}

/* Force the frozen zone (and its backdrop) onto their own GPU compositing layer at all times, not only once the JS above happens to toggle position:fixed. Without this, Chrome can leave the frozen zone's painted bitmap stuck at its pre-fixed spot after a hard refresh - layout/hit-testing is correct, but the pixels don't repaint as you scroll - even though the same zone renders fine after an in-app (client-side) navigation, which never hits this race. Pure rendering hint, no layout change. */
.freeze-zone, .freeze-zone-backdrop{will-change:transform;}

/* Keep header labels on one line so the frozen header row (.freeze-zone) never grows to fit a wrapped label - that extra height was the blank gap showing up above the data rows once frozen. No effect on labels that already fit on one line. */
.freeze-zone .ab-text{white-space:nowrap;overflow:hidden;text-overflow:ellipsis;}