@charset "UTF-8";
:root {
  --bf-content-max: none;
  --bf-content-narrow: 800px;
  --bf-page-gutter: clamp(16px, 4vw, 48px);
  --bf-header-h: 60px;
  --bf-sticky-top: calc(var(--bf-header-h) + 16px);
  /*
   * Motion. Three durations and one curve, because the eight `transition:` declarations this app
   * had were already written three ways — `120ms`, `0.15s`, `0.2s` — which is how `.btn` started.
   *
   * The scale is deliberately short. The user this is built for is a clerk at a loading bay with a
   * box in one hand; motion that delays them is a cost, not polish. `fast` is for a colour or a
   * hover, `base` for something that moves or resizes, `slow` only for something entering the page.
   * Nothing here should make an interaction feel slower than doing it instantly.
   *
   * The curve is M3's standard easing: quick to start, settles gently, never overshoots. A bouncy
   * curve reads as playful, and nothing about a recall search is playful.
   */
  --bf-motion-fast: 120ms;
  --bf-motion-base: 180ms;
  --bf-motion-slow: 320ms;
  --bf-ease: cubic-bezier(0.2, 0, 0, 1);
}

/*
 * Honour the operating system's "reduce motion" setting.
 *
 * This is not a preference about taste. Vestibular disorders make movement genuinely unpleasant,
 * and this app ran an `infinite` pulse on the expiry badge — on the rows that matter most, on the
 * screen people keep open. There was no way to turn it off.
 *
 * Near-zero rather than `none` so that anything listening for `transitionend`/`animationend` still
 * gets its event and does not hang waiting for one.
 */
@media (prefers-reduced-motion: reduce) {
  *,
  *::before,
  *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
    scroll-behavior: auto !important;
  }
}
html,
body {
  margin: 0;
  padding: 0;
  font-family: open sans, sans-serif;
  font-size: 14px;
}

html {
  scrollbar-gutter: stable;
}

body {
  background: var(--bf-content-bg, #f4faff);
  color: var(--bf-text, #0d1e25);
}

/*
 * Text for a screen reader and nobody else.
 *
 * `display: none` and `visibility: hidden` both remove an element from the accessibility tree as
 * well as the page, so neither works: this clips the box to nothing instead, which every assistive
 * technology still reads. Used by the loading skeletons, whose bars are `aria-hidden` — without a
 * label a blind user gets silence where a sighted one sees the page working.
 */
.sr-only {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}

/*
 * A padded text field must fit the column it is given.
 *
 * `box-sizing` is the one form-control default browsers still disagree on. Gecko's `forms.css`
 * spends a `border-box` on `select`, `textarea`'s scrollbox, `input[type=search]`, radio and
 * checkbox — and never on `input[type=text]`, which is therefore left at the CSS initial value,
 * `content-box`. Blink sets it for every input. So `width: 100%` plus padding — which is how all
 * eighteen of this app's form stylesheets write a field — is the full column in one engine and the
 * column *plus* 26px of padding and border in the other.
 *
 * The overflow is invisible in a single-column form and obvious in a two-column grid, where the
 * left field eats the gutter and butts against its neighbour: reported on Edit Item, where the
 * `<select>` sat correctly at 366px between inputs measuring 392px.
 *
 * Global, and deliberately not the universal `*` reset: several pages size a container with
 * `max-width` plus padding and are laid out expecting content-box, so resetting everything would
 * silently renarrow them. Only controls are normalised here.
 */
input,
textarea {
  box-sizing: border-box;
}

/*
 * A native `<select>` draws its dropdown arrow inside its padding box, so symmetric padding leaves
 * the glyph hard against the field's right border (pre-production 6.10).
 *
 * Global rather than per-component on purpose: twelve templates carry a `<select>` and exactly two
 * had compensated — privately, in their own SCSS, with their own value. That is the duplication 6.2
 * records for `.btn`, and a third private copy would have been the wrong shape of fix.
 *
 * Right side only. The left inset is deliberately untouched: several of these fields carry a
 * leading icon positioned at `left: 12px`, so the left padding is load-bearing rather than
 * incidental, and widening it would push the text off its icon.
 */
select {
  padding-right: 32px;
}

/*
 * The Material datepicker attached to the app's own date fields (pre-production 6.11).
 *
 * Only the *calendar* is Material here — the fields keep their existing chrome rather than being
 * rebuilt as `mat-form-field`. Adopting Material's form field would have restyled every date input
 * into a second visual language beside the hand-rolled ones next to them, widening exactly the
 * problem 6.2 records. The complaint was the calendar, so the calendar is what changed.
 *
 * Global because these appear across six components and component styles are scoped.
 */
.field__date {
  position: relative;
  display: flex;
  flex: 1;
  align-items: center;
}
.field__date input {
  flex: 1;
  min-width: 0;
}

/*
 * Positioned against whichever wrapper it sits in. Deliberately not paired with layout rules on
 * `.field__control`: that class is the *input itself* on several screens rather than a wrapper, so
 * giving it flex here would rearrange fields that have nothing to do with dates.
 */
.mat-datepicker-toggle {
  position: absolute;
  right: 4px;
  color: var(--bf-text-dim);
}

/*
 * The one button definition (pre-production 6.2).
 *
 * This was copy-pasted into seventeen components and redefined independently in each, and the copies
 * had drifted: five different padding values and two gaps, so the same button was a different size
 * depending on which page you were on. It also produced a real bug — the dashboard's copy omitted
 * `text-decoration: none`, and because its header actions are the app's only anchor-styled buttons,
 * they rendered underlined.
 *
 * Global rather than a partial each component imports: only three of the eighteen files imported the
 * shared partial that already held this, so a partial is a definition every component can *choose* to
 * ignore, which is how seventeen copies happened in the first place.
 *
 * Settled values, chosen by plurality rather than preference: `9px 16px` padding (9 of 17 copies) and
 * `6px` gap. Screens that had drifted to 10px/18px/20px change by a couple of pixels — that change is
 * the point, not a side effect.
 */
.btn {
  display: inline-flex;
  gap: 6px;
  align-items: center;
  justify-content: center;
  padding: 9px 16px;
  border-radius: 8px;
  font-size: 14px;
  font-weight: 600;
  cursor: pointer;
  flex-shrink: 0;
  white-space: nowrap;
  text-decoration: none;
  transition: background var(--bf-motion-fast) var(--bf-ease), border-color var(--bf-motion-fast) var(--bf-ease), color var(--bf-motion-fast) var(--bf-ease), box-shadow var(--bf-motion-fast) var(--bf-ease);
  /*
   * A press the finger can feel. One pixel down and a fractional scale — enough to register as
   * contact, small enough that nobody notices it happening. This is the whole of "tactile": the
   * temptation is a bigger number, and a button that visibly shrinks reads as a toy.
   */
}
.btn:active:not(:disabled) {
  transform: scale(0.985);
}
.btn .material-symbols-outlined {
  font-size: 18px;
}
.btn--primary {
  border: 1px solid var(--bf-brand);
  background: var(--bf-brand);
  color: var(--bf-on-fill);
}
.btn--primary:hover:not(:disabled) {
  background: var(--bf-brand-hover);
  border-color: var(--bf-brand-hover);
}
.btn--primary:disabled {
  opacity: 0.6;
  cursor: default;
}
.btn--stroked {
  border: 1px solid var(--bf-border);
  background: transparent;
  color: var(--bf-text);
}
.btn--stroked:hover:not(:disabled) {
  background: var(--bf-hover);
}
.btn--danger {
  border: 1px solid var(--bf-danger);
  background: var(--bf-danger);
  color: var(--bf-on-fill);
}
.btn--danger:hover:not(:disabled) {
  background: color-mix(in srgb, var(--bf-danger) 85%, black);
  border-color: color-mix(in srgb, var(--bf-danger) 85%, black);
}
.btn--danger:disabled {
  opacity: 0.6;
  cursor: default;
}

/*
 * The icon-only button, consolidated the same way `.btn` was (pre-production 6.2).
 *
 * It had drifted further than `.btn` ever did: **five** independent definitions — team,
 * export-schedules, alerts and storage-zones each declared `.icon-btn`, and `settings_screen.scss`
 * declared the same thing as `.iconbtn`. Two spellings meant a screen could copy the wrong one and
 * silently get no styling at all, which is a worse failure than the couple-of-pixels drift.
 *
 * Settled by plurality where the copies disagreed: a **34px square** (3 of 5; the two that used
 * `padding: 6px` let the glyph decide the hit area, so the target changed size with the icon) and an
 * 18px glyph to match `.btn` above.
 *
 * One deliberate departure from plurality: the icon lightens to `--bf-text` on hover. Only two
 * copies did that, but a dim icon sitting on a lit background reads as disabled — the hover says
 * "this is inert" at the moment it means the opposite.
 */
.icon-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 34px;
  height: 34px;
  border: none;
  border-radius: 8px;
  background: transparent;
  color: var(--bf-text-dim);
  cursor: pointer;
}
.icon-btn .material-symbols-outlined {
  font-size: 18px;
}
.icon-btn:hover:not(:disabled) {
  background: var(--bf-hover);
  color: var(--bf-text);
}
.icon-btn:disabled {
  opacity: 0.6;
  cursor: default;
}
.icon-btn--danger:hover:not(:disabled) {
  color: #c62828;
}

/*
 * The filter chip — a pill-shaped toggle above a list, as on the FEFO queue and the sweeps runs.
 *
 * Global for the reason `.btn` and `.icon-btn` are (pre-production 6.2): eight components had
 * declared `.chip` privately, and the ninth — compliance/sweep-runs — used the class name without
 * declaring it, so its whole state filter rendered as bare browser buttons. That is the same failure
 * the `.icon-btn` / `.iconbtn` split produced: copying a name without its styles gives *no* styling,
 * which is louder than drift but just as easy to ship.
 *
 * Settled by plurality across the four interactive copies (expiry-board, aging-stock, batch-trace,
 * fefo-queue), which agreed exactly: 6px 12px, 13px, weight 600.
 *
 * **`.chip` is overloaded, and this only claims half of it.** The other four copies — team,
 * suppliers, manufacturers, storage-zones — use the same name for a *static badge* at roughly half
 * the size and weight 700. They are a different control and should be `.badge`, which already exists
 * on the billing screen. Renaming them touches four working screens and belongs in its own change;
 * until then their local declarations override this one and nothing about them moves.
 */
.chips {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
}

.chip {
  padding: 6px 12px;
  border: 1px solid var(--bf-border);
  border-radius: 999px;
  background: transparent;
  color: var(--bf-text-dim);
  font-size: 13px;
  font-weight: 600;
  cursor: pointer;
  transition: border-color var(--bf-motion-fast) var(--bf-ease), background var(--bf-motion-fast) var(--bf-ease), color var(--bf-motion-fast) var(--bf-ease);
}
.chip:hover:not(:disabled) {
  background: var(--bf-hover);
}
.chip--active {
  border-color: var(--bf-brand);
  background: color-mix(in srgb, var(--bf-brand) 10%, transparent);
  color: var(--bf-brand);
}

/*
 * One keyboard focus ring, for everything.
 *
 * The app had six `:focus-visible` rules across forty-six stylesheets, so most controls fell back
 * to whatever the browser draws — which differs per engine and vanishes entirely against a dark
 * card in some of them. Anyone driving this by keyboard, which in a kitchen with gloves on is not
 * unusual, could lose their place.
 *
 * `:focus-visible` rather than `:focus`, so a mouse click does not leave a ring behind; the offset
 * puts it outside the border rather than on top of it, and `--bf-brand` keeps it recognisable as
 * this app's own colour rather than a system default.
 */
:where(a, button, input, select, textarea, summary, [tabindex]):focus-visible {
  outline: 2px solid var(--bf-brand);
  outline-offset: 2px;
  border-radius: 4px;
}

/*
 * The tint left behind by `appFlashOnChange` (frontend/app/shared/directives).
 *
 * Global because the directive can be put on any element and Angular scopes component styles — a
 * rule in the screen's own SCSS would not match markup the directive touched in a child component.
 *
 * It fades *out* rather than in. A value that lights up and stays lit demands attention it does not
 * deserve a second later; one that flares and settles says "this is the thing that just moved" and
 * then gets out of the way. `background-color` only, so it cannot reflow a table mid-update.
 */
.is-flashing,
.is-flashing-up,
.is-flashing-down {
  animation: bf-flash var(--bf-motion-slow) var(--bf-ease);
  animation-duration: 900ms;
  border-radius: 4px;
}

.is-flashing {
  --bf-flash-tint: var(--bf-brand);
}

.is-flashing-up {
  --bf-flash-tint: var(--bf-ok);
}

.is-flashing-down {
  --bf-flash-tint: var(--bf-warn);
}

@keyframes bf-flash {
  from {
    background-color: color-mix(in srgb, var(--bf-flash-tint) 28%, transparent);
  }
  to {
    background-color: transparent;
  }
}
/*
 * Printing a page (the FEFO pick list, backlog 22): the app's chrome stays on screen. The header and
 * footer are shell components outside any page, so only a global rule can reach them; `.no-print`
 * is for a page's own controls — the toolbar with the Print button has no business on the paper.
 */
@media print {
  app-main-header,
  app-main-footer,
  .no-print {
    display: none !important;
  }
  html,
  body,
  .app-main {
    background: #fff !important;
  }
  @page {
    margin: 12mm;
  }
}
.global-search-panel .mat-mdc-dialog-surface {
  border-radius: 12px;
  overflow: hidden;
}

/*# sourceMappingURL=global.css.map */
