/* ==========================================================================
   Components — every interactive element ships with default, hover, focus,
   active, disabled, loading, error. Half a component is not a component.
   ========================================================================== */

/* ==========================================================================
   Button
   ========================================================================== */

.btn {
  --btn-h: 32px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--s-2);
  height: var(--btn-h);
  padding: 0 var(--s-4);
  border-radius: var(--r-sm);
  border: 1px solid var(--line-strong);
  /* A secondary button sits on a white panel, so a white fill with a hairline
     made it read as an outline rather than something pressable. The faint
     top-lit gradient and a hair of elevation give it a surface. */
  background: linear-gradient(var(--raised), color-mix(in oklch, var(--shell) 70%, var(--raised)));
  box-shadow: var(--shadow-xs);
  color: var(--ink);
  font-size: var(--fs-xs);
  font-weight: 500;
  line-height: 1;
  white-space: nowrap;
  cursor: pointer;
  user-select: none;
  position: relative;
  transition:
    background-color var(--dur-hover) ease,
    border-color var(--dur-hover) ease,
    color var(--dur-hover) ease,
    box-shadow var(--dur-hover) ease,
    transform var(--dur-press) var(--ease-out);
}

.btn svg { width: 15px; height: 15px; }

@media (hover: hover) and (pointer: fine) {
  .btn:hover {
    background: linear-gradient(var(--raised), var(--shell));
    border-color: var(--ink-muted);
  }
}
/* Pressed reads as pressed: the light goes flat and the lift goes away. */
.btn:active { box-shadow: none; background: var(--shell); }

/* Instant feedback — the interface confirms it heard you */
.btn:active { transform: scale(0.97); }

.btn:disabled,
.btn[aria-disabled='true'] {
  cursor: not-allowed;
  opacity: 0.45;
  transform: none;
}
@media (hover: hover) and (pointer: fine) {
  .btn:disabled:hover { background: var(--raised); border-color: var(--line-strong); }
}

/* -- variants -- */

.btn-primary {
  background: linear-gradient(color-mix(in oklch, var(--primary) 88%, var(--raised)), var(--primary));
  border-color: var(--primary-active);
  color: var(--ink-inverse);
  box-shadow: var(--shadow-sm);
}
@media (hover: hover) and (pointer: fine) {
  .btn-primary:hover {
    background: linear-gradient(color-mix(in oklch, var(--primary-hover) 88%, var(--raised)), var(--primary-hover));
    border-color: var(--primary-active);
  }
}
.btn-primary:active { background: var(--primary-active); box-shadow: none; }

.btn-ghost {
  border-color: transparent;
  background: none;
  box-shadow: none;
  color: var(--ink-2);
}
.btn-ghost:active { background: var(--shell-2); }
@media (hover: hover) and (pointer: fine) {
  .btn-ghost:hover { background: var(--shell-2); color: var(--ink); border-color: transparent; }
}

.btn-danger {
  background: var(--canvas);
  border-color: var(--critical-line);
  color: var(--critical);
}
@media (hover: hover) and (pointer: fine) {
  .btn-danger:hover { background: var(--critical-soft); border-color: var(--critical); }
}

/* -- sizes -- */

.btn-sm  { --btn-h: 26px; padding: 0 var(--s-3); font-size: var(--fs-2xs); }
.btn-sm svg { width: 13px; height: 13px; }
.btn-lg  { --btn-h: 40px; padding: 0 var(--s-5); font-size: var(--fs-sm); }

.btn-icon { padding: 0; width: var(--btn-h); }

/* -- loading: the label stays put, a spinner replaces it -- */

.btn[data-loading='true'] {
  color: transparent;
  pointer-events: none;
}
.btn[data-loading='true']::after {
  content: '';
  position: absolute;
  inset: 0;
  margin: auto;
  width: 14px; height: 14px;
  border: 2px solid color-mix(in oklch, currentColor 25%, transparent);
  border-top-color: var(--ink-2);
  border-radius: 50%;
  animation: spin 620ms linear infinite;
}
.btn-primary[data-loading='true']::after {
  border-color: oklch(1 0 0 / 0.3);
  border-top-color: oklch(1 0 0 / 0.95);
}

@keyframes spin { to { transform: rotate(360deg); } }

/* -- button group: segmented, shared borders -- */

.btn-group { display: inline-flex; }
.btn-group .btn { border-radius: 0; margin-left: -1px; }
.btn-group .btn:first-child { border-radius: var(--r-sm) 0 0 var(--r-sm); margin-left: 0; }
.btn-group .btn:last-child  { border-radius: 0 var(--r-sm) var(--r-sm) 0; }
.btn-group .btn[aria-pressed='true'] {
  background: var(--primary-soft);
  border-color: var(--primary-line);
  color: var(--primary-ink);
  z-index: var(--z-raised);
}

/* ==========================================================================
   Fields
   ========================================================================== */

.field { display: flex; flex-direction: column; }
.field + .field { margin-top: var(--s-4); }

.input,
.select,
.textarea {
  width: 100%;
  height: 32px;
  padding: 0 var(--s-3);
  border: 1px solid var(--line-strong);
  border-radius: var(--r-sm);
  background: var(--canvas);
  color: var(--ink);
  font-size: var(--fs-xs);
  transition: border-color var(--dur-hover) ease, background-color var(--dur-hover) ease;
}

.textarea { height: auto; min-height: 68px; padding: var(--s-3); line-height: var(--lh-body); resize: vertical; }

.input::placeholder,
.textarea::placeholder { color: var(--ink-muted); opacity: 1; }

@media (hover: hover) and (pointer: fine) {
  .input:hover, .select:hover, .textarea:hover { border-color: var(--ink-muted); }
}

.input:focus-visible,
.select:focus-visible,
.textarea:focus-visible {
  outline: 2px solid var(--focus-ring);
  outline-offset: -1px;
  border-color: var(--focus-ring);
}

.input:disabled,
.select:disabled,
.textarea:disabled {
  background: var(--shell-2);
  color: var(--ink-muted);
  cursor: not-allowed;
}

.input[aria-invalid='true'],
.select[aria-invalid='true'] {
  border-color: var(--critical);
  background: var(--critical-soft);
}

.field-error {
  display: flex;
  align-items: flex-start;
  gap: var(--s-2);
  margin-top: var(--s-2);
  font-size: var(--fs-2xs);
  color: var(--critical);
}

.field-hint {
  margin-top: var(--s-2);
  font-size: var(--fs-2xs);
  color: var(--ink-muted);
}

.select {
  appearance: none;
  padding-right: var(--s-7);
  background-image: linear-gradient(45deg, transparent 50%, currentColor 50%),
                    linear-gradient(135deg, currentColor 50%, transparent 50%);
  background-position: right 13px center, right 8px center;
  background-size: 5px 5px, 5px 5px;
  background-repeat: no-repeat;
  cursor: pointer;
}

/* -- input with a leading affix (search, currency) -- */

.input-wrap { position: relative; display: flex; align-items: center; }
.input-wrap > svg {
  position: absolute;
  left: var(--s-3);
  width: 14px; height: 14px;
  color: var(--ink-muted);
  pointer-events: none;
}
.input-wrap > .input { padding-left: 30px; }

.input-affix {
  position: absolute;
  right: var(--s-3);
  font-size: var(--fs-2xs);
  color: var(--ink-muted);
  pointer-events: none;
  font-family: var(--font-data);
}

/* ==========================================================================
   Dimension input — whole inches + fraction picker
   This is how Bali, Hunter Douglas and Blinds.com actually take an order.
   Decimal inches would betray the prototype on the first screen.
   ========================================================================== */

.dim {
  display: flex;
  align-items: stretch;
  border: 1px solid var(--line-strong);
  border-radius: var(--r-sm);
  background: var(--canvas);
  overflow: hidden;
  transition: border-color var(--dur-hover) ease;
  width: fit-content;
}

@media (hover: hover) and (pointer: fine) {
  .dim:hover { border-color: var(--ink-muted); }
}
.dim:focus-within { border-color: var(--focus-ring); box-shadow: inset 0 0 0 1px var(--focus-ring); }

.dim-whole {
  width: 58px;
  height: 32px;
  border: none;
  background: transparent;
  text-align: right;
  padding: 0 var(--s-2) 0 var(--s-3);
  font-family: var(--font-data);
  font-variant-numeric: tabular-nums;
  font-size: var(--fs-sm);
  font-weight: 500;
  -moz-appearance: textfield;
}
.dim-whole::-webkit-outer-spin-button,
.dim-whole::-webkit-inner-spin-button { appearance: none; margin: 0; }
.dim-whole:focus-visible { outline: none; }

.dim-frac {
  height: 32px;
  border: none;
  border-left: 1px solid var(--line);
  background: var(--shell);
  padding: 0 var(--s-5) 0 var(--s-2);
  font-family: var(--font-data);
  font-size: var(--fs-2xs);
  color: var(--ink-2);
  cursor: pointer;
  appearance: none;
  background-image: linear-gradient(45deg, transparent 50%, currentColor 50%),
                    linear-gradient(135deg, currentColor 50%, transparent 50%);
  background-position: right 10px center, right 6px center;
  background-size: 4px 4px, 4px 4px;
  background-repeat: no-repeat;
}
.dim-frac:focus-visible { outline: none; background-color: var(--primary-soft); }

.dim-unit {
  display: flex;
  align-items: center;
  padding: 0 var(--s-3);
  border-left: 1px solid var(--line);
  background: var(--shell);
  font-size: var(--fs-2xs);
  color: var(--ink-muted);
}

.dim[data-invalid='true'] { border-color: var(--critical); }

/* -- read-only dimension display, e.g. in a table cell -- */
.dim-out {
  font-family: var(--font-data);
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}

/* The "ordered → manufactured" chip. Nothing is ever deducted invisibly. */
.size-chip {
  display: inline-flex;
  align-items: center;
  gap: var(--s-2);
  font-family: var(--font-data);
  font-size: var(--fs-2xs);
  white-space: nowrap;
}
.size-chip .arrow { color: var(--ink-muted); }
.size-chip .mfg { color: var(--primary-ink); font-weight: 500; }

/* ==========================================================================
   Checkbox / radio / switch
   ========================================================================== */

.check {
  display: inline-flex;
  align-items: flex-start;
  gap: var(--s-3);
  cursor: pointer;
  font-size: var(--fs-xs);
  line-height: 1.4;
}
.check input {
  appearance: none;
  width: 15px; height: 15px;
  flex-shrink: 0;
  margin-top: 1px;
  border: 1px solid var(--line-strong);
  border-radius: var(--r-xs);
  background: var(--canvas);
  cursor: pointer;
  display: grid;
  place-content: center;
  transition: background-color var(--dur-hover) ease, border-color var(--dur-hover) ease;
}
.check input[type='radio'] { border-radius: 50%; }

.check input::before {
  content: '';
  width: 8px; height: 8px;
  transform: scale(0);
  transition: transform var(--dur-press) var(--ease-out);
  background: var(--ink-inverse);
  clip-path: polygon(14% 46%, 0 60%, 40% 100%, 100% 22%, 86% 8%, 38% 70%);
}
.check input[type='radio']::before {
  clip-path: none;
  border-radius: 50%;
  width: 6px; height: 6px;
}

.check input:checked {
  background: var(--primary);
  border-color: var(--primary);
}
.check input:checked::before { transform: scale(1); }

@media (hover: hover) and (pointer: fine) {
  .check:hover input:not(:disabled) { border-color: var(--ink-muted); }
}
.check input:focus-visible { outline: 2px solid var(--focus-ring); outline-offset: 2px; }
.check input:disabled { background: var(--shell-2); cursor: not-allowed; opacity: 0.6; }
.check:has(input:disabled) { cursor: not-allowed; color: var(--ink-muted); }

.switch {
  appearance: none;
  width: 32px; height: 18px;
  border-radius: 9px;
  background: var(--line-strong);
  position: relative;
  cursor: pointer;
  flex-shrink: 0;
  transition: background-color var(--dur-press) ease;
}
.switch::after {
  content: '';
  position: absolute;
  top: 2px; left: 2px;
  width: 14px; height: 14px;
  border-radius: 50%;
  background: oklch(1 0 0);
  box-shadow: var(--shadow-sm);
  transition: transform var(--dur-press) var(--ease-out);
}
.switch:checked { background: var(--primary); }
.switch:checked::after { transform: translateX(14px); }
.switch:focus-visible { outline: 2px solid var(--focus-ring); outline-offset: 2px; }

/* ==========================================================================
   Status — colour is never the only signal.
   Each status carries a shape-differentiated glyph AND a text label, so it
   survives greyscale, colour-vision deficiency and a bad projector.
   ========================================================================== */

.st {
  display: inline-flex;
  align-items: center;
  gap: var(--s-2);
  font-size: var(--fs-2xs);
  font-weight: 500;
  white-space: nowrap;
  line-height: 1.4;
}

.st::before {
  content: '';
  width: 8px; height: 8px;
  flex-shrink: 0;
}

/* Covered / on-time / in-spec — the normal state. Deliberately unpainted. */
.st-ok { color: var(--ink-muted); }
.st-ok::before {
  border: 1.5px solid var(--ink-muted);
  border-radius: 50%;
  background: transparent;
}

/* Shortage / past-due / blocked — filled square */
.st-critical { color: var(--critical); }
.st-critical::before { background: var(--critical); border-radius: 1px; }

/* At risk — triangle */
.st-warning { color: var(--warning-ink); }
.st-warning::before {
  background: var(--warning);
  clip-path: polygon(50% 0, 100% 100%, 0 100%);
  width: 9px; height: 8px;
}

/* Informational / planned — filled circle */
.st-info { color: var(--info); }
.st-info::before { background: var(--info); border-radius: 50%; }

/* Released / firm — filled diamond */
.st-firm { color: var(--primary-ink); }
.st-firm::before { background: var(--primary); transform: rotate(45deg); width: 7px; height: 7px; }

/* ==========================================================================
   Badge / chip / kbd
   ========================================================================== */

.badge {
  display: inline-flex;
  align-items: center;
  gap: var(--s-1);
  height: 19px;
  padding: 0 var(--s-2);
  border-radius: var(--r-xs);
  border: 1px solid var(--line);
  background: var(--shell);
  color: var(--ink-2);
  font-size: var(--fs-3xs);
  font-weight: 500;
  letter-spacing: var(--tracking-label);
  white-space: nowrap;
}

.badge-critical { background: var(--critical-soft); border-color: var(--critical-line); color: var(--critical); }
.badge-warning  { background: var(--warning-soft);  border-color: var(--warning-line);  color: var(--warning-ink); }
.badge-primary  { background: var(--primary-soft);  border-color: var(--primary-line);  color: var(--primary-ink); }
.badge-accent   { background: var(--accent-soft);   border-color: var(--accent-line);   color: var(--accent-ink); }
.badge-info     { background: var(--info-soft);     border-color: var(--info-line);     color: var(--info); }

.badge-num { font-family: var(--font-data); font-variant-numeric: tabular-nums; }

.chip {
  display: inline-flex;
  align-items: center;
  gap: var(--s-2);
  height: 24px;
  padding: 0 var(--s-2) 0 var(--s-3);
  border: 1px solid var(--line);
  border-radius: var(--r-sm);
  background: var(--shell);
  font-size: var(--fs-2xs);
  color: var(--ink-2);
}
.chip button {
  display: grid; place-content: center;
  width: 16px; height: 16px;
  border-radius: var(--r-xs);
  color: var(--ink-muted);
  cursor: pointer;
}
@media (hover: hover) and (pointer: fine) {
  .chip button:hover { background: var(--shell-2); color: var(--ink); }
}

kbd, .kbd {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 18px;
  height: 18px;
  padding: 0 5px;
  border: 1px solid var(--line-strong);
  border-bottom-width: 2px;
  border-radius: var(--r-xs);
  background: var(--canvas);
  font-family: var(--font-data);
  font-size: var(--fs-3xs);
  color: var(--ink-2);
  line-height: 1;
}

/* ==========================================================================
   Table — the most-used surface in the app
   ========================================================================== */

.table-wrap {
  overflow-x: auto;
  overscroll-behavior-x: contain;
  border-radius: 0 0 var(--r-md) var(--r-md);
}

table.tbl {
  width: 100%;
  border-collapse: separate;
  border-spacing: 0;
  font-size: var(--fs-xs);
}

.tbl caption {
  text-align: left;
  padding: var(--s-3) var(--s-5);
  font-size: var(--fs-2xs);
  color: var(--ink-muted);
}
.tbl caption.visually-hidden { padding: 0; }

.tbl thead th {
  position: sticky;
  top: 0;
  z-index: var(--z-sticky);
  background: linear-gradient(var(--shell), color-mix(in oklch, var(--shell) 78%, var(--raised)));
  padding: var(--s-3) var(--s-4);
  text-align: left;
  font-size: var(--fs-3xs);
  font-weight: 600;
  color: var(--ink-2);
  letter-spacing: 0.05em;
  text-transform: uppercase;
  white-space: nowrap;
  border-bottom: 1px solid var(--line-strong);
  /* Rows scroll under the header, so it needs to sit above them visibly. */
  box-shadow: 0 1px 0 var(--line-strong), 0 4px 8px -6px oklch(0.22 0.012 143 / 0.30);
}

.tbl tbody td {
  padding: var(--s-3) var(--s-4);
  border-bottom: 1px solid var(--line-faint);
  vertical-align: middle;
}

.tbl tbody tr:last-child td { border-bottom: none; }

/* Hover is colour only. No transform — a row that nudges under the cursor
   in a 200-row table is a layout shift, not a delight. */
.tbl tbody tr {
  transition: background-color var(--dur-hover) ease;
}
@media (hover: hover) and (pointer: fine) {
  .tbl tbody tr:hover { background: var(--shell); }
}

.tbl tbody tr[aria-selected='true'] { background: var(--primary-soft); }

/* The focused row carries a brass rail on its leading edge — the accent's
   one job. */
.tbl tbody tr.is-focus td:first-child { box-shadow: inset 3px 0 0 var(--accent); }

.tbl tbody tr.is-clickable { cursor: pointer; }

/* Exception rows. The tint is a whisper — the .st glyph does the real work,
   and the row only needs to be findable when scanning a MIXED list. Applying
   it to a table where every row is an exception washes the whole thing out;
   don't. */
.tbl tbody tr.row-critical { background: color-mix(in oklch, var(--critical-soft) 72%, var(--raised)); }
.tbl tbody tr.row-warning  { background: color-mix(in oklch, var(--warning-soft) 72%, var(--raised)); }
@media (hover: hover) and (pointer: fine) {
  .tbl tbody tr.row-critical:hover { background: color-mix(in oklch, var(--critical-soft) 80%, var(--critical-line)); }
  .tbl tbody tr.row-warning:hover  { background: color-mix(in oklch, var(--warning-soft) 80%, var(--warning-line)); }
}

/* Numeric columns */
.tbl .n {
  font-family: var(--font-data);
  font-variant-numeric: tabular-nums;
  text-align: right;
  white-space: nowrap;
}
.tbl th.n { text-align: right; }

/* Sticky first column so the item code stays visible while scrolling wide
   time-phased grids sideways. */
.tbl .sticky-col {
  position: sticky;
  left: 0;
  z-index: calc(var(--z-sticky) - 1);
  background: var(--canvas);
}
.tbl thead .sticky-col { z-index: calc(var(--z-sticky) + 1); background: var(--shell); }
.tbl tbody tr:hover .sticky-col { background: var(--shell); }
.tbl tbody tr[aria-selected='true'] .sticky-col { background: var(--primary-soft); }
.tbl .sticky-col::after {
  content: '';
  position: absolute;
  top: 0; right: 0; bottom: 0;
  width: 1px;
  background: var(--line);
}

/* Sortable header */
.tbl th.sortable { cursor: pointer; user-select: none; }
@media (hover: hover) and (pointer: fine) {
  .tbl th.sortable:hover { color: var(--ink); }
}
.tbl th[aria-sort] { color: var(--ink); }
.tbl th[aria-sort]::after {
  content: '';
  display: inline-block;
  margin-left: var(--s-2);
  border: 3px solid transparent;
  vertical-align: middle;
}
.tbl th[aria-sort='ascending']::after  { border-bottom-color: var(--ink-2); margin-bottom: 3px; }
.tbl th[aria-sort='descending']::after { border-top-color: var(--ink-2); margin-top: 3px; }

/* A recalculated row flashes rather than animating its digits — moving
   numbers are noise, a settling background is information. */
@keyframes row-flash {
  from { background-color: var(--accent-soft); }
  to   { background-color: transparent; }
}
.tbl tbody tr.flash { animation: row-flash var(--dur-flash) var(--ease-out); }

/* Compact density for the time-phased grid */
.tbl-dense tbody td,
.tbl-dense thead th { padding: var(--s-2) var(--s-3); font-size: var(--fs-2xs); }

/* ==========================================================================
   Tabs — no animation. Used constantly; movement would only slow it down.
   ========================================================================== */

.tabs {
  display: flex;
  gap: var(--s-1);
  border-bottom: 1px solid var(--line);
  padding: 0 var(--s-5);
  overflow-x: auto;
  scrollbar-width: none;
}
.tabs::-webkit-scrollbar { display: none; }

.tab {
  padding: var(--s-3) var(--s-3);
  border-bottom: 2px solid transparent;
  margin-bottom: -1px;
  font-size: var(--fs-xs);
  font-weight: 500;
  color: var(--ink-muted);
  cursor: pointer;
  white-space: nowrap;
  transition: color var(--dur-hover) ease, border-color var(--dur-hover) ease;
}
@media (hover: hover) and (pointer: fine) {
  .tab:hover { color: var(--ink); }
}
.tab[aria-selected='true'] {
  color: var(--ink);
  border-bottom-color: var(--primary);
}
.tab:focus-visible { outline-offset: -2px; }

/* Segmented control — for view switches (Day / Week / Month).
   A long set (one button per stock item, say) scrolls inside itself rather
   than pushing the page sideways. */
.seg {
  display: inline-flex;
  padding: 2px;
  gap: 2px;
  background: var(--shell-2);
  border-radius: var(--r-sm);
  max-width: 100%;
  overflow-x: auto;
  overscroll-behavior-x: contain;
  scrollbar-width: none;
}
.seg::-webkit-scrollbar { display: none; }
.seg button { flex-shrink: 0; }
.seg button {
  padding: var(--s-1) var(--s-3);
  border-radius: var(--r-xs);
  font-size: var(--fs-2xs);
  font-weight: 500;
  color: var(--ink-muted);
  cursor: pointer;
  transition: background-color var(--dur-hover) ease, color var(--dur-hover) ease;
  height: 22px;
}
@media (hover: hover) and (pointer: fine) {
  .seg button:hover { color: var(--ink); }
}
.seg button[aria-pressed='true'] {
  background: var(--canvas);
  color: var(--ink);
  box-shadow: var(--shadow-sm);
}

/* ==========================================================================
   Popover / menu — scales from its trigger, never from centre
   ========================================================================== */

.popover {
  position: fixed;
  z-index: var(--z-dropdown);
  min-width: 190px;
  max-width: min(340px, calc(100vw - 24px));
  padding: var(--s-1);
  background: var(--raised);
  border: 1px solid var(--line);
  border-radius: var(--r-md);
  box-shadow: var(--shadow-pop);
  transform-origin: var(--pop-origin, top left);
  transition: opacity var(--dur-pop) var(--ease-out), transform var(--dur-pop) var(--ease-out);
}

/* Nothing in the real world appears from nothing — 0.97, never 0. */
.popover[hidden] { display: none; }
@starting-style {
  .popover { opacity: 0; transform: scale(0.97); }
}
.popover.closing { opacity: 0; transform: scale(0.97); }

.menu-item {
  display: flex;
  align-items: center;
  gap: var(--s-3);
  width: 100%;
  padding: var(--s-2) var(--s-3);
  border-radius: var(--r-xs);
  font-size: var(--fs-xs);
  color: var(--ink-2);
  cursor: pointer;
  text-align: left;
  transition: background-color var(--dur-hover) ease, color var(--dur-hover) ease;
}
.menu-item svg { width: 14px; height: 14px; color: var(--ink-muted); }
@media (hover: hover) and (pointer: fine) {
  .menu-item:hover { background: var(--shell-2); color: var(--ink); }
}
.menu-item[aria-current='true'] { color: var(--primary-ink); font-weight: 500; }
.menu-item.is-danger { color: var(--critical); }
.menu-item.is-danger svg { color: var(--critical); }
.menu-sep { height: 1px; background: var(--line); margin: var(--s-1) 0; }
.menu-label {
  padding: var(--s-2) var(--s-3) var(--s-1);
  font-size: var(--fs-3xs);
  font-weight: 600;
  color: var(--ink-muted);
  letter-spacing: var(--tracking-label);
}

/* ==========================================================================
   Tooltip
   ========================================================================== */

.tip {
  position: fixed;
  z-index: var(--z-tooltip);
  max-width: 260px;
  padding: var(--s-2) var(--s-3);
  background: var(--ink);
  color: var(--canvas);
  border-radius: var(--r-sm);
  font-size: var(--fs-2xs);
  line-height: 1.4;
  pointer-events: none;
  box-shadow: var(--shadow-pop);
  transform-origin: var(--pop-origin, bottom center);
  transition: opacity 125ms var(--ease-out), transform 125ms var(--ease-out);
}
.tip[hidden] { display: none; }
@starting-style { .tip { opacity: 0; transform: scale(0.97); } }
/* Once one tooltip is open, adjacent ones open instantly. Feels faster
   without defeating the purpose of the initial delay. */
.tip[data-instant='true'] { transition-duration: 0ms; }

/* ==========================================================================
   Drawer — the detail surface. Modals are usually laziness; a drawer keeps
   the list visible behind it so the planner never loses their place.
   ========================================================================== */

.drawer-backdrop {
  position: fixed;
  inset: 0;
  z-index: var(--z-drawer-backdrop);
  background: var(--overlay);
  transition: opacity var(--dur-drawer) var(--ease-out);
}
.drawer-backdrop[hidden] { display: none; }
@starting-style { .drawer-backdrop { opacity: 0; } }
.drawer-backdrop.closing { opacity: 0; }

.drawer {
  position: fixed;
  top: 0; right: 0; bottom: 0;
  z-index: var(--z-drawer);
  width: min(560px, 100vw);
  display: flex;
  flex-direction: column;
  background: var(--canvas);
  border-left: 1px solid var(--line);
  box-shadow: var(--shadow-lg);
  transition: transform var(--dur-drawer) var(--ease-drawer),
              opacity var(--dur-drawer) var(--ease-drawer);
}
.drawer[hidden] { display: none; }
/* translateY/X in percent is relative to the element's own size — works
   regardless of the drawer's width. */
@starting-style { .drawer { transform: translateX(100%); } }
.drawer.closing { transform: translateX(100%); }

.drawer-hd {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: var(--s-4);
  padding: var(--s-5);
  border-bottom: 1px solid var(--line);
}
.drawer-bd { flex: 1; overflow-y: auto; padding: var(--s-5); overscroll-behavior: contain; }
.drawer-ft {
  display: flex;
  gap: var(--s-3);
  justify-content: flex-end;
  padding: var(--s-4) var(--s-5);
  border-top: 1px solid var(--line);
  background: var(--shell);
}

@media (max-width: 640px) {
  .drawer { width: 100vw; }
}

/* ==========================================================================
   Dialog — centred, so transform-origin stays centre (unlike popovers)
   ========================================================================== */

.dialog-backdrop {
  position: fixed;
  inset: 0;
  z-index: var(--z-modal-backdrop);
  background: var(--overlay);
  display: grid;
  place-items: center;
  padding: var(--s-5);
  transition: opacity var(--dur-pop) var(--ease-out);
}
.dialog-backdrop[hidden] { display: none; }
@starting-style { .dialog-backdrop { opacity: 0; } }
.dialog-backdrop.closing { opacity: 0; }

.dialog {
  width: min(460px, 100%);
  background: var(--canvas);
  border: 1px solid var(--line);
  border-radius: var(--r-lg);
  box-shadow: var(--shadow-lg);
  transition: opacity var(--dur-pop) var(--ease-out), transform var(--dur-pop) var(--ease-out);
}
@starting-style { .dialog { opacity: 0; transform: scale(0.96); } }
.dialog-backdrop.closing .dialog { opacity: 0; transform: scale(0.96); }

.dialog-bd { padding: var(--s-6); }
.dialog-ft {
  display: flex; gap: var(--s-3); justify-content: flex-end;
  padding: var(--s-4) var(--s-6);
  border-top: 1px solid var(--line);
}

/* ==========================================================================
   Toast — transitions not keyframes, so rapid stacking retargets smoothly
   ========================================================================== */

.toast-region {
  position: fixed;
  bottom: var(--s-5);
  right: var(--s-5);
  z-index: var(--z-toast);
  display: flex;
  flex-direction: column-reverse;
  gap: var(--s-3);
  pointer-events: none;
  max-width: min(380px, calc(100vw - 32px));
}

.toast {
  display: flex;
  align-items: flex-start;
  gap: var(--s-3);
  padding: var(--s-4);
  background: var(--raised);
  border: 1px solid var(--line-strong);
  border-radius: var(--r-md);
  box-shadow: var(--shadow-pop);
  font-size: var(--fs-xs);
  pointer-events: auto;
  transition: opacity 260ms var(--ease-out), transform 260ms var(--ease-out);
}
@starting-style { .toast { opacity: 0; transform: translateY(12px); } }
.toast.closing { opacity: 0; transform: translateY(8px); }

.toast svg { width: 16px; height: 16px; margin-top: 1px; }
.toast-ok svg       { color: var(--primary); }
.toast-critical svg { color: var(--critical); }
.toast-warning svg  { color: var(--warning-ink); }

/* ==========================================================================
   Tree — pegging and where-used
   ========================================================================== */

.tree { font-size: var(--fs-xs); }

.tree-node > .tree-row {
  display: flex;
  align-items: center;
  gap: var(--s-2);
  padding: var(--s-2) var(--s-3);
  border-radius: var(--r-xs);
  cursor: pointer;
  transition: background-color var(--dur-hover) ease;
}
@media (hover: hover) and (pointer: fine) {
  .tree-node > .tree-row:hover { background: var(--shell); }
}

.tree-toggle {
  display: grid; place-content: center;
  width: 16px; height: 16px;
  flex-shrink: 0;
  color: var(--ink-muted);
  cursor: pointer;
}
.tree-toggle svg { width: 11px; height: 11px; transition: transform var(--dur-pop) var(--ease-out); }
.tree-node[data-open='true'] > .tree-row > .tree-toggle svg { transform: rotate(90deg); }

.tree-children {
  margin-left: 8px;
  padding-left: var(--s-4);
  border-left: 1px solid var(--line);
}
.tree-node[data-open='false'] > .tree-children { display: none; }

/* ==========================================================================
   Meter / progress — used for capacity load and yield
   ========================================================================== */

.meter {
  height: 6px;
  border-radius: 3px;
  background: var(--shell-2);
  overflow: hidden;
  position: relative;
}
.meter-fill {
  height: 100%;
  background: var(--primary);
  border-radius: 3px;
  transition: width 300ms var(--ease-out);
}
.meter-fill.over { background: var(--critical); }
.meter-fill.near { background: var(--warning); }

/* The 100% mark on a capacity bar that can exceed 100% */
.meter-mark {
  position: absolute;
  top: -2px; bottom: -2px;
  width: 1px;
  background: var(--ink-2);
}

/* ==========================================================================
   Skeleton — reserve the space, don't spin in the middle of content
   ========================================================================== */

.skel {
  background: linear-gradient(
    90deg,
    var(--shell-2) 25%,
    color-mix(in oklch, var(--shell-2) 55%, var(--canvas)) 50%,
    var(--shell-2) 75%
  );
  background-size: 200% 100%;
  animation: skel 1.3s ease-in-out infinite;
  border-radius: var(--r-xs);
  height: 12px;
}
@keyframes skel { to { background-position: -200% 0; } }
@media (prefers-reduced-motion: reduce) {
  .skel { animation: none; background: var(--shell-2); }
}

/* ==========================================================================
   Empty state — teaches the interface rather than saying "nothing here"
   ========================================================================== */

.empty {
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
  gap: var(--s-3);
  padding: var(--s-10) var(--s-6);
  color: var(--ink-muted);
}
.empty-glyph {
  width: 34px; height: 34px;
  color: var(--line-strong);
}
.empty h3 { font-size: var(--fs-sm); color: var(--ink-2); }
.empty p { font-size: var(--fs-xs); max-width: 46ch; }

/* ==========================================================================
   Callout — inline explanation. Full border, never a side stripe.
   ========================================================================== */

.callout {
  display: flex;
  align-items: flex-start;
  gap: var(--s-3);
  padding: var(--s-4);
  border: 1px solid var(--line);
  border-radius: var(--r-sm);
  background: var(--shell);
  font-size: var(--fs-xs);
  line-height: 1.5;
  color: var(--ink-2);
}
.callout svg { width: 15px; height: 15px; margin-top: 1px; flex-shrink: 0; color: var(--ink-muted); }
.callout strong { color: var(--ink); font-weight: 600; }

.callout-critical { background: var(--critical-soft); border-color: var(--critical-line); color: var(--ink); }
.callout-critical svg { color: var(--critical); }
.callout-warning { background: var(--warning-soft); border-color: var(--warning-line); color: var(--ink); }
.callout-warning svg { color: var(--warning-ink); }
.callout-primary { background: var(--primary-soft); border-color: var(--primary-line); color: var(--ink); }
.callout-primary svg { color: var(--primary); }

/* ==========================================================================
   Metric — a number and what it means. Never a gradient hero tile.
   ========================================================================== */

/* Three distinct tiers — label, figure, footnote — so the number is
   unmistakably the content and the other two get out of its way. */
.metric { display: flex; flex-direction: column; gap: var(--s-2); }
.metric-k {
  font-size: var(--fs-2xs);
  color: var(--ink-muted);
  font-weight: 500;
  letter-spacing: var(--tracking-label);
}
.metric-v {
  font-family: var(--font-data);
  font-variant-numeric: tabular-nums;
  font-size: var(--fs-3xl);
  font-weight: 500;
  line-height: 1;
  letter-spacing: -0.03em;
  color: var(--ink);
}
.metric-note { font-size: var(--fs-2xs); color: var(--ink-faint); line-height: 1.45; }

/* ==========================================================================
   Definition list — the workhorse for spec panels
   ========================================================================== */

.dl { display: grid; grid-template-columns: minmax(0, auto) minmax(0, 1fr); gap: var(--s-2) var(--s-5); font-size: var(--fs-xs); }
.dl dt { color: var(--ink-muted); }
.dl dd { margin: 0; text-align: right; min-width: 0; overflow-wrap: anywhere; }
.dl dd.n { font-family: var(--font-data); font-variant-numeric: tabular-nums; }

/* ==========================================================================
   Nesting strip — the hero visual. A stock bar drawn to scale with its
   cuts, kerf and offcut tail. This, not a donut chart.
   ========================================================================== */

.nest-bar {
  position: relative;
  height: 30px;
  display: flex;
  border: 1px solid var(--line-strong);
  border-radius: var(--r-xs);
  background: var(--shell-2);
  overflow: hidden;
}

.nest-piece {
  position: relative;
  border-right: 1px solid var(--canvas);
  background: var(--primary-soft);
  display: flex;
  align-items: center;
  justify-content: center;
  font-family: var(--font-data);
  font-size: var(--fs-3xs);
  color: var(--primary-ink);
  overflow: hidden;
  cursor: default;
  transition: background-color var(--dur-hover) ease;
  min-width: 0;
}
@media (hover: hover) and (pointer: fine) {
  .nest-piece:hover { background: var(--primary-line); }
}
.nest-piece.from-remnant {
  background: var(--accent-soft);
  color: var(--accent-ink);
}
@media (hover: hover) and (pointer: fine) {
  .nest-piece.from-remnant:hover { background: var(--accent-line); }
}

/* The offcut tail — hatched, because waste should look like waste */
.nest-waste {
  background-image: repeating-linear-gradient(
    -45deg,
    var(--line) 0 1px,
    transparent 1px 6px
  );
  background-color: var(--shell-2);
  flex-shrink: 0;
}
/* A tail long enough to return to the remnant register reads differently
   from true scrap */
.nest-waste.reusable {
  background-image: repeating-linear-gradient(
    -45deg,
    var(--accent-line) 0 1px,
    transparent 1px 6px
  );
}

/* Fabric roll layout — 2D, drops laid across the roll width */
.roll-map {
  position: relative;
  border: 1px solid var(--line-strong);
  border-radius: var(--r-xs);
  background: var(--shell-2);
  overflow: hidden;
}
.roll-drop {
  position: absolute;
  background: var(--primary-soft);
  border: 1px solid var(--primary-line);
  border-radius: 1px;
  font-family: var(--font-data);
  font-size: var(--fs-3xs);
  color: var(--primary-ink);
  display: flex;
  align-items: center;
  justify-content: center;
  overflow: hidden;
}
/* Pattern-repeat rounding waste — the metres the client is currently
   throwing away without knowing it */
.roll-repeat-waste {
  position: absolute;
  background-image: repeating-linear-gradient(-45deg, var(--warning-line) 0 1px, transparent 1px 5px);
  background-color: var(--warning-soft);
  border: 1px solid var(--warning-line);
  border-radius: 1px;
}

/* ==========================================================================
   Log stream — the MRP run console
   ========================================================================== */

.log {
  font-family: var(--font-data);
  font-size: var(--fs-2xs);
  line-height: 1.7;
  color: var(--ink-2);
  padding: var(--s-4) var(--s-5);
  max-height: 340px;
  overflow-y: auto;
  overscroll-behavior: contain;
}

.log-line {
  display: flex;
  gap: var(--s-4);
  white-space: pre-wrap;
  opacity: 0;
  transform: translateY(8px);
  animation: log-in 260ms var(--ease-out) forwards;
}
@keyframes log-in { to { opacity: 1; transform: translateY(0); } }
@media (prefers-reduced-motion: reduce) {
  .log-line { opacity: 1; transform: none; animation: none; }
}

.log-t { color: var(--ink-muted); flex-shrink: 0; }
.log-line.is-head { color: var(--ink); font-weight: 500; }
.log-line.is-warn { color: var(--warning-ink); }
.log-line.is-crit { color: var(--critical); }
.log-line.is-done { color: var(--primary-ink); font-weight: 500; }

/* ==========================================================================
   Toolbar — filter row above every table
   ========================================================================== */

.toolbar {
  display: flex;
  align-items: center;
  gap: var(--s-3);
  flex-wrap: wrap;
  padding: var(--s-3) var(--s-5);
  border-bottom: 1px solid var(--line);
  background: var(--canvas);
}
