/* ══════════════════════════════════════════════════════════════════════════
   Industry — DowntimeInsight application layer ("Control Room", handoff 1a).

   Loaded AFTER bootstrap.css and industry-tokens.css, and only by the three
   Industry layouts (_AppLayout, _TerminalLayout, _AuthLayout). Pages still on
   the legacy layout never see it.

   Bootstrap is kept for its grid and its JS components (modal, dropdown,
   collapse) — every view depends on `row`/`col-*`. That means several of our
   class names collide with Bootstrap's (.btn, .input/.form-control, .table,
   .card). Load order handles most of it; the "Bootstrap reconciliation"
   section below handles the rules Bootstrap wins on specificity.
   ══════════════════════════════════════════════════════════════════════════ */

/* ── Bootstrap reconciliation ─────────────────────────────────────────────
   Bootstrap ships rounded corners, its own focus ring, and high-specificity
   button states (.btn-primary:not(:disabled):not(.disabled):active). Square
   corners are the point of this design, so kill radius broadly and re-assert
   our states at matching specificity. */

.industry .btn, .industry .form-control, .industry .input, .industry .card,
.industry .modal-content, .industry .dropdown-menu, .industry .badge,
.industry .custom-select, .industry .alert, .industry .list-group-item,
.industry .page-link, .industry .close, .industry .nav-link, .industry img.avatar,
.industry .progress, .industry .progress-bar, .industry .tag, .industry .seg,
.industry .input-group-text, .industry .table td, .industry .table th {
  border-radius: 0 !important;
}

.industry .btn:focus, .industry .btn.focus,
.industry .form-control:focus, .industry .input:focus {
  box-shadow: none;
}
.industry .btn:focus-visible,
.industry .form-control:focus-visible {
  outline: 2px solid var(--color-accent); outline-offset: 2px;
}

.industry .btn-primary:not(:disabled):not(.disabled):active,
.industry .btn-primary:not(:disabled):not(.disabled).active {
  background: var(--color-accent-700); border-color: var(--color-accent-700); color: var(--color-bg);
}
.industry .btn-secondary:not(:disabled):not(.disabled):active,
.industry .btn-secondary:not(:disabled):not(.disabled).active {
  background: color-mix(in srgb, var(--color-text) 14%, transparent);
  border-color: var(--color-divider); color: var(--color-text);
}

/* Bootstrap's .form-control is used throughout the existing views; make it read
   as our .input rather than rewriting every field's class. */
.industry .form-control,
.industry select.form-control,
.industry textarea.form-control {
  min-height: 39.6px; padding: 6.6px 11px;
  font-family: var(--font-body); font-size: 15.4px;
  color: var(--color-text); background: var(--color-surface);
  border: 1px solid var(--color-divider);
  transition: border-color 120ms ease;
}
.industry .form-control:hover { border-color: color-mix(in srgb, var(--color-text) 45%, transparent); }
.industry .form-control:focus { border-color: var(--color-accent); background: var(--color-surface); color: var(--color-text); }
.industry .form-control::placeholder { color: var(--color-text-faint); }
.industry .form-control:-webkit-autofill,
.industry .form-control:-webkit-autofill:hover,
.industry .form-control:-webkit-autofill:focus {
  -webkit-box-shadow: 0 0 0 44px var(--color-surface) inset;
  -webkit-text-fill-color: var(--color-text);
}
.industry label { font-family: var(--font-body); }

/* No drop shadows on in-page surfaces — cards are line drawings, not panels. */
.industry .card, .industry .shadow, .industry .shadow-sm, .industry .shadow-lg {
  box-shadow: none !important;
  background: transparent;
  border: 1px solid var(--color-divider);
}
.industry .bg-white { background: transparent !important; }

/* Bootstrap modals become our .dialog. */
.industry .modal-content {
  background: var(--color-bg); border: 1px solid var(--color-divider);
  box-shadow: var(--shadow-lg);
}
.industry .modal-header, .industry .modal-footer { border-color: var(--color-divider); }
.industry .modal-title { font-family: var(--font-heading); font-weight: 600; }

.industry .dropdown-menu {
  background: var(--color-bg); border: 1px solid var(--color-divider);
  box-shadow: var(--shadow-md); font-size: 15.4px; padding: var(--space-1) 0;
}
.industry .dropdown-item { color: var(--color-text); padding: 7.7px 15.4px; }
.industry .dropdown-item:hover, .industry .dropdown-item:focus {
  background: rgba(29, 31, 32, .04); color: var(--color-text);
}

/* ── Base ────────────────────────────────────────────────────────────────── */

.industry {
  margin: 0;
  background: var(--color-bg);
  color: var(--color-text);
  font-family: var(--font-body);
  font-size: 16.5px; line-height: 1.55; font-weight: 400;
}
.industry h1, .industry h2, .industry h3, .industry h4, .industry h5, .industry h6 {
  font-family: var(--font-heading); font-weight: 600;
  line-height: 1.12; letter-spacing: -0.015em;
}

/* ── App shell ───────────────────────────────────────────────────────────── */

.app-shell {
  display: grid;
  grid-template-columns: 231px 1fr;
  min-height: 100vh;
  transition: grid-template-columns 160ms ease;
}
.app-shell.is-collapsed { grid-template-columns: 61.6px 1fr; }

/* ── Sidebar ─────────────────────────────────────────────────────────────── */

.app-sidebar {
  background: var(--color-accent-900);
  color: #e9edf1;
  padding: 19.8px 0;
  display: flex; flex-direction: column;
  position: sticky; top: 0; height: 100vh;
  overflow: hidden;
}
.app-sidebar a { color: inherit; text-decoration: none; }
.app-sidebar :focus-visible { outline: 2px solid #fff; outline-offset: -2px; }

.app-brand {
  padding: 0 19.8px 22px;
  display: flex; flex-direction: column; gap: 2px;
  border-bottom: 1px solid rgba(255, 255, 255, .12);
}
.app-brand-mark { display: flex; align-items: center; gap: 9.9px; color: #fff; }
.app-brand-name { font-family: var(--font-heading); font-weight: 600; font-size: 24.2px; line-height: 1; color: #fff; }
.app-brand-sub { color: var(--color-accent-400); font-size: 12.1px; letter-spacing: .34em; }

.app-nav { display: flex; flex-direction: column; padding: 15.4px 0; gap: 1px; overflow-y: auto; }
.app-nav-section {
  padding: 19.8px 19.8px 8.8px;
  color: rgba(255, 255, 255, .38);
}
.app-nav-section:first-child { padding-top: 6.6px; }
.app-nav-item {
  display: flex; align-items: center; gap: 12.1px;
  padding: 9.9px 19.8px 9.9px 23.1px;
  font-size: 15.4px; color: rgba(255, 255, 255, .72);
  border-left: 3px solid transparent;
  transition: background 120ms ease, color 120ms ease;
  white-space: nowrap;
}
.app-nav-item:hover { background: rgba(255, 255, 255, .07); color: #fff; text-decoration: none; }
.app-nav-item.is-active {
  background: var(--color-accent); color: #fff;
  border-left: 3px solid #fff; padding-left: 19.8px;
}
.app-nav-item svg { flex: none; }

.app-site {
  margin-top: auto; padding: 15.4px 19.8px 0;
  border-top: 1px solid rgba(255, 255, 255, .12);
  display: flex; flex-direction: column; gap: 3.3px;
  background: none; border-left: 0; border-right: 0; border-bottom: 0;
  text-align: left; width: 100%; cursor: pointer;
  color: inherit; font: inherit;
}
.app-site:hover .app-site-name { color: var(--color-accent-300); }
.app-site-label { color: rgba(255, 255, 255, .38); }
.app-site-name { font-size: 15.4px; color: #fff; transition: color 120ms ease; }

/* Collapsed rail */
.is-collapsed .app-brand { padding: 0 0 22px; align-items: center; }
.is-collapsed .app-brand-name, .is-collapsed .app-brand-sub,
.is-collapsed .app-nav-section, .is-collapsed .app-nav-item span,
.is-collapsed .app-site-label, .is-collapsed .app-site-name { display: none; }
.is-collapsed .app-nav-item { justify-content: center; padding: 9.9px 0; border-left-width: 0; }
.is-collapsed .app-nav-item.is-active { border-left: 3px solid #fff; padding-left: 0; }
.is-collapsed .app-site { padding: 15.4px 0 0; align-items: center; }

/* ── Top bar ─────────────────────────────────────────────────────────────── */

.app-main { display: flex; flex-direction: column; min-width: 0; }

.app-topbar {
  height: 61.6px; flex: none;
  display: flex; align-items: center; gap: 17.6px;
  padding: 0 24.2px;
  background: var(--color-bg);
  border-bottom: 1px solid var(--color-divider);
}
.app-topbar-search { position: relative; flex: 1; max-width: 374px; }
.app-topbar-search .form-control, .app-topbar-search .input { padding-left: 33px; }
.app-topbar-search svg { position: absolute; left: 9.9px; top: 11px; color: var(--color-text-faint); }
.app-topbar-right { margin-left: auto; display: flex; align-items: center; gap: 15.4px; }
.app-clock { font-family: var(--font-mono); font-variant-numeric: tabular-nums; font-size: 13.2px; color: var(--color-text-muted); white-space: nowrap; }

.app-sidebar-toggle {
  width: 33px; height: 33px; padding: 0; flex: none;
  display: inline-flex; align-items: center; justify-content: center;
  background: transparent; border: 1px solid transparent; cursor: pointer;
  color: var(--color-text-muted);
  transition: background 120ms ease, color 120ms ease;
}
.app-sidebar-toggle:hover { background: rgba(29, 31, 32, .07); color: var(--color-text); }

.app-user { display: flex; align-items: center; gap: 9.9px; padding-left: 15.4px; border-left: 1px solid var(--color-divider); }
.app-avatar {
  width: 28.6px; height: 28.6px; flex: none;
  display: inline-flex; align-items: center; justify-content: center;
  background: var(--color-accent-800); color: #fff;
  font-family: var(--font-mono); font-size: 12.1px; line-height: 1;
}
.app-user-name { font-size: 14.3px; color: var(--color-text); }

/* ── Content frame ───────────────────────────────────────────────────────── */

.app-content {
  padding: 24.2px;
  display: flex; flex-direction: column; gap: 19.8px;
  background: var(--color-bg);
  flex: 1; min-width: 0;
}

.page-head { display: flex; align-items: flex-end; justify-content: space-between; gap: 17.6px; flex-wrap: wrap; }
.page-eyebrow { color: var(--color-accent-700); display: block; margin-bottom: 2px; }
.industry .page-title { font-size: 33px; letter-spacing: -.01em; margin: 0; }
.page-actions { display: flex; align-items: center; gap: 11px; flex-wrap: wrap; }

/* ── Panels ──────────────────────────────────────────────────────────────── */

/* Full-bleed page content: replaces Bootstrap's .container, which centred and capped the width
   so a page sat inset from its own title while its siblings did not. Just a vertical stack. */
.page-block { display: flex; flex-direction: column; gap: 17.6px; }

.panel { background: transparent; }
.panel-pad { padding: 15.4px 17.6px; }
.panel-head {
  display: flex; align-items: center; justify-content: space-between; gap: 13.2px;
  padding: 13.2px 17.6px; border-bottom: 1px solid var(--color-divider);
}
.panel-head h4 { margin: 0; font-size: 17.6px; }
.panel-scope { color: var(--color-text-muted); }

.chart-head {
  display: flex; justify-content: space-between; align-items: baseline; gap: 13.2px;
  margin-bottom: 8.8px;
}
.chart-head h4 { margin: 0; font-size: 17.6px; }

/* ── KPI tiles ───────────────────────────────────────────────────────────── */

.kpi-strip { display: grid; grid-template-columns: repeat(4, 1fr); gap: 15.4px; }
.kpi { padding: 15.4px 17.6px; display: flex; flex-direction: column; gap: 3.3px; }
.kpi-label { color: var(--color-text-muted); }
.kpi-value {
  font-family: var(--font-mono); font-variant-numeric: tabular-nums;
  font-size: 37.4px; font-weight: 500; line-height: 1.1;
}
.kpi-unit { font-size: 18.7px; color: var(--color-text-muted); }
.kpi-sub { font-size: 13.2px; color: var(--color-text-muted); }
/* The Monitored tile goes solid red the moment one machine is down — the machine is the thing that
   actually stops (DEC-0056). Same red as the operator band and the plant board's down tiles. */
.kpi-alarm-fill { background: #991b1b; }
.kpi-alarm-fill .kpi-value { color: #fff; }
.kpi-alarm-fill .kpi-label, .kpi-alarm-fill .kpi-sub, .kpi-alarm-fill .kpi-unit { color: #fecaca; }
.kpi-alarm-fill .kpi-sub a { color: #fff; text-decoration: underline; }

/* Machine roster — the machines and their live states at the foot of a line screen. A line reads
   idle off-shift, but a stopped machine on it still shows DOWN here (DEC-0056). */
.machine-roster .roster-line { padding: 10px 0; border-top: 1px solid var(--color-border); }
.machine-roster .roster-line:first-of-type { border-top: none; }
.machine-roster .roster-line-name { margin-bottom: 6px; }
.machine-roster .roster-machines { display: flex; flex-wrap: wrap; gap: 8px; }
.machine-roster .roster-machine {
    display: inline-flex; align-items: center; gap: 8px;
    padding: 6px 10px; border: 1px solid var(--color-border); border-radius: 6px;
}
.machine-roster .roster-machine-name { font-weight: 500; }
.machine-roster .roster-since { color: var(--color-text-muted); }
/* The one filled panel on the dashboard — current-state alarm earns it. */
.kpi-invert { background: var(--color-accent-900); }
.kpi-invert .kpi-value { color: #fff; }
.kpi-invert .kpi-label, .kpi-invert .kpi-sub, .kpi-invert .kpi-unit { color: var(--color-accent-300); }
.kpi-invert.is-alarm .kpi-value { color: var(--color-alarm); }

/* ── Uptime bars (tonal severity, no hue) ────────────────────────────────── */

/* Lettering here is ~25% up on the original 14.3 / 13.2, in step with the canvas charts
   (IndustryChart.font) — this list is read the same way and from the same distance as the plots
   beside it. The name column grows with it, and is a minmax() rather than a fixed 105.6px so a
   full-width card spends its extra width on the names instead of on the track: these are the
   same "Wrapper 03" strings that crowd the Pareto axis, and here they can only be clipped. */
.uptime-list { display: flex; flex-direction: column; gap: 9.9px; }
.uptime-row {
  display: grid; grid-template-columns: minmax(110px, 26%) 1fr 63px;
  align-items: center; gap: 11px;
}
.uptime-name { font-size: 17.9px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.uptime-track { height: 8.8px; background: var(--color-track); }
.uptime-fill { height: 100%; background: var(--uptime-fair); }
.uptime-fill.is-critical { background: var(--uptime-critical); }
.uptime-fill.is-poor { background: var(--uptime-poor); }
.uptime-fill.is-fair { background: var(--uptime-fair); }
.uptime-fill.is-good { background: var(--uptime-good); }
.uptime-value {
  font-family: var(--font-mono); font-variant-numeric: tabular-nums;
  font-size: 16.5px; text-align: right;
}

/* ── Table pattern ───────────────────────────────────────────────────────── */

.table-toolbar {
  display: flex; align-items: center; justify-content: space-between; gap: 13.2px;
  padding: 13.2px 17.6px; border-bottom: 1px solid var(--color-divider); flex-wrap: wrap;
}
.table-search { position: relative; max-width: 286px; flex: 1 1 198px; }
.table-search .form-control, .table-search .input { padding-left: 33px; }
.table-search svg { position: absolute; left: 9.9px; top: 11px; color: var(--color-text-faint); }

.industry .table { margin: 0; font-size: 15.4px; }
.industry .table th { padding: 11px 13.2px; }
/* Numeric and date columns must not wrap - a split figure is unreadable - but text headers may. */
.industry .table th.num, .industry .table td.num, .industry .table td.mono { white-space: nowrap; }
.industry .table td { padding: 11px 13.2px; font-size: 14.9px; vertical-align: middle; }
.industry .table tbody tr { transition: background 120ms ease; }
/* Filled header, on the sidebar's own ground so every dark surface on screen is the same dark.
   The handoff dropped the filled header bar; in practice a wide list needs its row of labels held
   together, and Hour by Hour had already gone this way on its own. One rule now, so no table can
   drift: same ground, same white, same size and tracking everywhere. */
.industry .table thead { background: var(--color-accent-900); }
.industry .table thead th {
  border-top: 0; border-bottom: 0;
  color: #fff;
  font-size: 13.2px; letter-spacing: .1em;
  vertical-align: middle;
}
/* No zebra striping, no filled header bar. */
.industry .table-striped tbody tr:nth-of-type(odd) { background: transparent; }
.industry .table-bordered, .industry .table-bordered td, .industry .table-bordered th { border: 0; }
.industry .table-bordered td, .industry .table-bordered th { border-bottom: 1px solid var(--color-divider); }
/* All figures mono and right-aligned. */
/* Cells only. The class marks a *column* as numeric, and it used to hit that column's <th> too,
   so a header like "Created" came out in mono at the data size while its neighbours stayed in the
   heading face — one label in the wrong type. A header is a label whatever the column holds; it
   takes the alignment and nothing else. */
.industry .table td.num, .industry .table td.mono {
  font-family: var(--font-mono); font-variant-numeric: tabular-nums;
  text-align: right; font-size: 14.3px;
}
.industry .table th.num, .industry .table th.mono { text-align: right; }
/* A timestamp is a label, not a figure to compare down the column, so it reads better ranged left
   under its heading than pushed to the far edge of the cell. Pairs with .mono - the mono face and
   tabular figures are what keep timestamps scannable - and only overrides the alignment. Must stay
   after the .mono rule: same specificity, so source order is what decides. */
.industry .table td.mono-left, .industry .table th.mono-left { text-align: left; }

.row-actions { display: flex; align-items: center; justify-content: flex-end; gap: 4.4px; }
.btn-row {
  width: 30.8px; height: 30.8px; padding: 0; flex: none;
  display: inline-flex; align-items: center; justify-content: center;
  background: transparent; border: 1px solid transparent;
  color: var(--color-text-muted); cursor: pointer;
  transition: background 120ms ease, color 120ms ease;
}
.btn-row:hover { background: rgba(89, 128, 166, .10); color: var(--color-accent); text-decoration: none; }
.btn-row:active { background: rgba(89, 128, 166, .18); }

.table-pager {
  display: flex; align-items: center; justify-content: space-between; gap: 13.2px;
  padding: 13.2px 17.6px; border-top: 1px solid var(--color-divider); flex-wrap: wrap;
}
.table-count { font-family: var(--font-mono); font-variant-numeric: tabular-nums; font-size: 13.2px; color: var(--color-text-muted); }
.table-pages { display: flex; gap: 4.4px; }
.table-pages .btn { width: 30.8px; height: 30.8px; padding: 0; font-size: 13.2px; font-family: var(--font-mono); }
.table-pages .btn.is-current { background: var(--color-accent); border-color: var(--color-accent); color: #fff; }

/* ── Tabs ────────────────────────────────────────────────────────────────── */

/* ── Overview ──────────────────────────────────────────────────────────────
   The range switch (.seg), the routed tab bar, and the two bar treatments the
   Executive view draws in HTML rather than with a chart library.

   WHY NO CHART LIBRARY HERE. Both Executive charts are a row of bars against a
   category axis with no interaction — the cost trend and the cause ranking. Drawn
   in CSS they cost no JavaScript, cannot shift the layout when the page refreshes
   on its 60s timer, and re-render from the server with the rest of the panel. A
   canvas would have to be torn down and rebuilt on every refresh to say the same
   thing. Charts that need axes, tooltips or two series still go through
   IndustryChart. */

/* A square ghost button carrying one icon — the target pencil in the line-health
   table. Revealed on row hover so a table of numbers stays a table of numbers, but
   ALWAYS visible on keyboard focus: an affordance that only exists under a pointer
   is one a keyboard user cannot reach. */
.btn-icon {
  width: 24.2px; height: 24.2px; padding: 0;
  display: inline-flex; align-items: center; justify-content: center;
  border: 0; background: transparent;
  color: var(--color-text-faint);
  vertical-align: middle;
}
.btn-icon:hover { color: var(--color-accent); background: rgba(89, 128, 166, .10); }
.target-cell .btn-icon { opacity: 0; }
tr:hover .target-cell .btn-icon,
.target-cell .btn-icon:focus-visible { opacity: 1; }
.target-cell { display: inline-flex; align-items: center; gap: 4.4px; justify-content: flex-end; }

.seg { display: inline-flex; flex: none; border: 1px solid var(--color-divider); }
.seg a {
  padding: 6.6px 13.2px;
  white-space: nowrap;
  font-family: var(--font-mono);
  font-size: 12.1px;
  color: var(--color-text-muted);
  text-decoration: none;
  border-right: 1px solid var(--color-divider);
}
.seg a:last-child { border-right: 0; }
.seg a:hover { color: var(--color-text); text-decoration: none; }
.seg a.is-current { background: var(--color-accent-900); color: #fff; }

/* The routed Overview tabs. Same look as .tabbar, but each tab is a real route
   rather than a panel toggle, so a manager can send a link to exactly what they
   are looking at. */
.overview-bar { display: flex; align-items: center; gap: 22px; border-bottom: 1px solid var(--color-text); margin-bottom: 17.6px; }
.overview-bar .tabbar { border-bottom: 0; flex: 1 1 auto; }
.overview-bar .overview-eyebrow { color: var(--color-text-muted); }
.overview-bar .overview-right { display: flex; align-items: center; gap: 13.2px; margin-left: auto; padding-bottom: 6.6px; }

/* The 13-week cost trend: one column per bucket, height proportional to the worst
   bucket in the range. */
.trend { display: flex; align-items: flex-end; gap: 3.3px; height: 176px; border-bottom: 1px solid var(--color-divider); }
.trend-col { flex: 1 1 0; display: flex; flex-direction: column; justify-content: flex-end; height: 100%; }
.trend-bar { background: var(--color-accent); min-height: 1px; }
/* The bucket the range ends inside is always the shortest bar and reads as a
   dramatic improvement until you notice it is Tuesday. Hatched, not hidden. */
.trend-col.is-partial .trend-bar {
  background: repeating-linear-gradient(45deg,
    var(--color-accent) 0 4px, var(--color-accent-400) 4px 8px);
}
.trend-labels { display: flex; gap: 3.3px; margin-top: 4.4px; }
.trend-label { flex: 1 1 0; text-align: center; font-family: var(--font-mono); font-size: 11px; color: var(--color-chart-tick); overflow: hidden; white-space: nowrap; }

/* "Where the money went": label, track, figure. */
.rank-row { display: grid; grid-template-columns: 1fr 88px; gap: 8.8px; align-items: center; padding: 7.7px 0; border-bottom: 1px solid var(--color-divider); }
.rank-row:last-child { border-bottom: 0; }
.rank-label { font-size: 13.2px; }
.rank-track { height: 8.8px; background: var(--color-track); margin-top: 4.4px; }
.rank-fill { height: 100%; background: var(--color-accent); }
.rank-value { font-family: var(--font-mono); font-variant-numeric: tabular-nums; text-align: right; font-size: 13.2px; }
.rank-meta { font-family: var(--font-mono); font-size: 11px; color: var(--color-text-muted); }

.tabbar { display: flex; gap: 0; border-bottom: 1px solid var(--color-divider); }
.tabbar a {
  padding: 9.9px 15.4px; text-decoration: none; color: var(--color-text-muted);
  border-bottom: 2px solid transparent; margin-bottom: -1px;
  font-family: var(--font-heading); font-weight: 600; font-size: 14px;
  letter-spacing: .14em; text-transform: uppercase;
  transition: color 120ms ease, border-color 120ms ease;
}
.tabbar a:hover { color: var(--color-text); }
.tabbar a.is-active { color: var(--color-text); border-bottom-color: var(--color-accent); }
.tabbar a:focus-visible { outline: 2px solid var(--color-accent); outline-offset: -2px; }

/* Tab panels: only the active one is in the document flow. Sections are server-rendered and
   switched client-side so a tab change costs nothing and keeps its scroll position. */
.tabpanel[hidden] { display: none; }

/* ── Chart card controls ─────────────────────────────────────────────────── */

/* The dashboard's per-widget control row: exclude-planned toggle, range select, and the
   custom-range date pair. These previously depended on checkbox.min.css / manager-dashboard.css
   (the .el-switch toggle) and on sb-admin-2, none of which this design loads — so the controls
   rendered invisible and unclickable. They are design-system controls now. */
.chart-controls {
  display: flex; align-items: center; flex-wrap: wrap; gap: 8.8px 13.2px;
  margin-bottom: 11px;
}
.chart-controls .form-control { max-width: 209px; min-height: 35.2px; padding: 4.4px 8.8px; font-size: 14.3px; }
.chart-controls input[type="date"] { max-width: 165px; font-family: var(--font-mono); font-size: 13.2px; }
.chart-controls .btn { min-height: 35.2px; padding: 4.4px 13.2px; font-size: 14.3px; }
.chart-range-word { color: var(--color-text-muted); font-size: 13.2px; }

/* The "Exclude planned" toggle's text. It was thirteen copies of an inline font-size:13px, which
   both undercut .radio's own 15.4px and put the only sizing decision for the label in the one
   place no stylesheet can reach. One class, ~27% up, and at full ink rather than inherited grey —
   it is a filter that changes what every figure on the card means, not a caption. */
.chart-filter-label { font-size: 16.5px; color: var(--color-text); }

/* The custom-range controls belong on their own line under the selectors: appearing to the
   right of the machine dropdown made them read as a separate, unrelated filter rather than as
   the detail of the range you just chose. flex-basis 100% forces the break; the group collapses
   to zero height while its children are hidden. */
.chart-range {
  flex: 0 0 100%;
  display: flex; align-items: center; flex-wrap: wrap; gap: 8.8px;
}
.chart-range .form-control { max-width: 170.5px; }
.chart-range p { margin: 0; font-size: 13.2px; }

/* ── Dashboard widgets end level ─────────────────────────────────────────────
   Two charts side by side must bottom out on the same line. Their content does not have the
   same height and cannot be made to: a Pareto may carry a Note ("showing the 12 of 30 …"), a
   chart with no data renders the short .state-empty instead of a 245px plot, and the climate
   card carries a readout its neighbours do not. So the card is stretched to the row rather than
   every panel being talked into producing identical content.

   Bootstrap's .row is already a flex container and already stretches its columns, so the column
   was the right height all along - it was the .card inside it that stopped at its content and
   left the border ending mid-air. flex-direction: column + flex on the card hands that height
   down; a short panel now takes the extra as space under its plot.

   This deliberately does NOT resize any plot. Charts here are non-responsive by design (see the
   note in industry-charts.js: the ResizeObserver loop) - a canvas is sized once from a container
   of known height, and widgets load ~100ms apart, so a plot that grew when a slower neighbour
   landed would keep the canvas it was drawn with. Fixed plot, stretched card. */
.dashboard-widget { display: flex; flex-direction: column; }
.dashboard-widget > .card { flex: 1 1 auto; }

/* ── Engine source line ──────────────────────────────────────────────────────
   The classification page's build-stamp diagnostic. Deliberately the smallest thing on the page:
   it reports "fine" almost always, and it previously occupied a full panel with a heading and two
   KPI tiles above the controls a manager actually came for. One muted line, its own label inline,
   and it only carries a tag when something is wrong. */
.engine-stamp-line {
  display: flex; align-items: baseline; flex-wrap: wrap; gap: 6.6px;
  margin: 0 0 var(--space-3);
  font-size: 12.1px; color: var(--color-text-muted);
}
.engine-stamp-line .lbl { color: var(--color-text-muted); }
.engine-stamp-line .mono { font-size: 12.1px; color: var(--color-text); }
.engine-stamp-sep { opacity: 0.5; }
/* A mismatch is the one state worth reading, so it takes full ink while the rest stays muted. */
.engine-stamp-line .tag-alarm ~ span { color: var(--color-text); }

/* ── Dashboard-wide controls ─────────────────────────────────────────────────
   The one row above the charts that sets time frame and machine for all of them. It reuses
   .chart-controls for the selectors so it cannot drift from the per-widget rows it writes into;
   only the action line below them is new. */
.dashboard-controls .chart-controls { margin-bottom: 0; }
.dashboard-controls-actions {
  display: flex; align-items: center; flex-wrap: wrap; gap: 8.8px 17.6px;
}
/* Prose, not a label: .lbl upper-cases and letter-spaces, which is right for "TIME FRAME" beside a
   control and wrong for a sentence. Muted and a size down, in normal case.
   .field-note is the same thing under a heading or beside a field, with room beneath it. */
.dashboard-controls-note,
.field-note {
  font-size: 13.2px; color: var(--color-text-muted); margin: 0;
}
/* Deliberately NOT flex. A flex container makes every child a flex ITEM, including the bare text
   nodes either side of the <strong> — which broke this one sentence into three columns. It is a
   paragraph; it should flow like one, with the icon simply inline. */
.field-note { margin-bottom: var(--space-3); }
.field-note svg { vertical-align: -2px; margin-right: 4.4px; }
.field-note strong { color: var(--color-text); font-weight: 600; }
/* The saved-default statement and the confirmation both sit at the end of the action line; only the
   first takes the auto margin, or the two would fight for it and shove the buttons about as the
   confirmation text appears and changes length. */
.dashboard-controls-actions #globalSavedDefault { margin-left: auto; text-align: right; }
.dashboard-controls-actions #globalControlsMessage { text-align: right; }

/* ── Busy ────────────────────────────────────────────────────────────────────
   One indicator for every operation that makes the user wait — the loading GIFs, the
   "Loading" chart caption and the relabelled buttons all resolve to this. Drive it from
   industry-busy.js (AppBusy.show/hide for the page, AppBusy.setBusy for one panel); the
   classes below are its only two placements. */

/* A hairline ring, not a gif. */
.spinner {
  display: inline-block; width: 19.8px; height: 19.8px; flex: none;
  border: 1.5px solid var(--color-track);
  border-top-color: var(--color-accent);
  border-radius: 50%;
  animation: industry-spin 700ms linear infinite;
}
.spinner-sm { width: 14.3px; height: 14.3px; }
@keyframes industry-spin { to { transform: rotate(360deg); } }

/* The card itself: opaque, so it reads as a thing sitting on the work rather than a wash over
   it. Identical in both placements — that is what makes the app feel like one app.

   On the accent-900 ground, the same one the charts' tooltips use. A card in the page's own
   grey disappeared into it: over a dimmed table it was carried only by its hairline border and
   read as "barely visible". This is the design system's existing answer for a transient thing
   floating above the work. */
.busy-panel {
  display: inline-flex; align-items: center; gap: 11.2px;
  padding: 13.2px 18.7px;
  background: var(--color-accent-900);
  border: 0;
  border-radius: var(--radius-md);
  box-shadow: var(--shadow-md);
  font-family: var(--font-heading); font-weight: 600; font-size: 12.1px;
  letter-spacing: .14em; text-transform: uppercase; color: #fff;
  white-space: nowrap;
}
.busy-panel .spinner {
  border-color: rgba(255, 255, 255, .28);
  border-top-color: var(--color-accent-300);
}

/* Stacked variant, for an operation that reports its progress: ring above the label, with
   sub-lines in normal case so a phase message stays readable. */
.busy-panel-stacked {
  flex-direction: column; gap: 9.9px; text-align: center;
  padding: 22.4px 29.9px; max-width: 380px;
}
.busy-note {
  font-family: var(--font-body); font-weight: 400; font-size: 12.8px;
  letter-spacing: normal; text-transform: none; color: rgba(255, 255, 255, .74);
  white-space: normal;
}
.busy-note:empty { display: none; }

/* Page level: a scrim that also swallows clicks, because the operation under it owns the page. */
.app-busy {
  position: fixed; inset: 0; z-index: 2000;
  display: flex; align-items: center; justify-content: center;
  background: color-mix(in srgb, var(--color-neutral-900) 22%, transparent);
}
.app-busy[hidden] { display: none; }

/* Panel level: the same card over one widget, which keeps its content (dimmed) so the layout
   does not jump while it refetches. */
/* min-height: on a first load the panel is still empty, and an absolutely positioned overlay
   adds no height of its own — without this the card would have nothing to show. */
.is-busy { position: relative; min-height: 132px; }
.is-busy > *:not(.busy-inline) { opacity: .45; transition: opacity 120ms ease; }
.busy-inline {
  position: absolute; inset: 0; z-index: 2;
  display: flex; align-items: center; justify-content: center;
  background: color-mix(in srgb, var(--color-bg) 72%, transparent);
}

/* In-flow variant, for a panel that has no content yet: same card, no scrim, holds the height
   so the page does not reflow when the data lands. */
.busy-block {
  display: flex; align-items: center; justify-content: center;
  padding: 24.2px 4.4px;
}

/* DataTables' own processing box is suppressed — industry-shell.js listens for the same event
   and draws the shared panel over the whole table instead. Selector depth matches DataTables'
   own `div.dataTables_wrapper div.dataTables_processing`, which would otherwise win. */
div.dataTables_wrapper div.dataTables_processing { display: none !important; }

/* The page-size control is a <label> holding "Show", the <select> and "entries" as bare text
   nodes. The select is a block-ish form-control, so the whitespace around it collapses and the
   words end up jammed against the box. Lay the label out as a flex row and let gap do the
   spacing — text runs become anonymous flex items, so both sides get it.

   Only the length control: the filter label holds a single input and is right-aligned by
   DataTables' own grid, which flex would break. */
.industry .dataTables_length label {
  display: flex; align-items: center; gap: 8.8px;
  margin-bottom: 0;
}
.industry .dataTables_length select { width: auto; flex: none; }

@media (prefers-reduced-motion: reduce) {
  .spinner { animation-duration: 2s; }
}

/* ── Climate: series key ─────────────────────────────────────────────────────
   Colour is the only thing separating temperature from humidity on the combined plot, so each
   reading carries its own swatch. This is the legend the design system suppresses on charts —
   here it sits on the readout, where it is a key rather than chart chrome. */
.series-key { width: 9.9px; height: 9.9px; flex: none; display: inline-block; }
.series-key.is-temp { background: var(--color-accent); }
.series-key.is-humidity { background: var(--color-series-2); }
.climate-key { display: inline-flex; align-items: center; gap: 7.7px; }

/* Combined layout (dashboard): readings on the left, series switch on the right. */
.climate-head {
  display: flex; align-items: center; justify-content: space-between;
  gap: 13.2px; flex-wrap: wrap; margin-bottom: 11px;
}
.climate-readings { display: flex; align-items: baseline; gap: 22px; flex-wrap: wrap; }
.climate-reading { display: inline-flex; align-items: baseline; gap: 7.7px; }
.climate-reading[hidden] { display: none; }
.climate-reading .series-key { align-self: center; }

/* ── Climate (Environment, split view) ───────────────────────────────────────
   Temperature and humidity get a plot each. One chart with two series was unreadable: the
   palette has a single hue, the design system suppresses legends, and a shared axis with two
   different units invites reading a humidity value as a temperature. Each panel is a plain
   hairline box - registration marks are reserved for top-level panels. */
.climate-split { display: grid; grid-template-columns: 1fr 1fr; gap: 13.2px; }
.climate-metric { border: 1px solid var(--color-divider); padding: 11px 13.2px; min-width: 0; }
.climate-metric-head { display: flex; align-items: baseline; justify-content: space-between; gap: 8px; margin-bottom: 6px; }
.climate-metric-head .lbl { color: var(--color-text-muted); }
.climate-value { font-size: 28.6px; font-weight: 500; line-height: 1.1; }
.climate-unit { font-size: 15.4px; color: var(--color-text-muted); margin-left: 2px; }
.climate-plot { position: relative; height: 132px; }
/* 245 is the dashboard's plot budget (the tilted x labels take ~60px of it - see
   industry-charts.js). This card is the only one that puts anything ABOVE its plot: the current
   readings and the Temp/Humidity/Both switch. Spending the 245 on the plot and adding the head
   on top made it ~45px taller than whatever chart shared its row, so the head comes OUT of the
   budget instead - the card ends level and this one plot is the shorter for it, which is the
   right trade for the panel that also tells you the current temperature.

   The plot is flexed rather than given `calc(245px - head)` because the head wraps at narrow
   widths and a hard-coded height would go wrong exactly when it wrapped. min-height keeps it a
   chart rather than a sliver if it ever wraps to two lines; that is the one case where the card
   is allowed to exceed 245 and stretch its neighbour instead. */
.climate-combined { height: 245px; display: flex; flex-direction: column; min-height: 0; }
.climate-plot-combined { height: 245px; }
.climate-combined > .climate-plot-combined { height: auto; flex: 1 1 auto; min-height: 160px; }

/* Stacked layout (Environment): full width, one measure under the other, tall enough to read
   a day's detail rather than just the shape. */
.climate-stack { display: flex; flex-direction: column; gap: 17.6px; }
.climate-stack .climate-metric { min-width: 0; }
.climate-plot-tall { height: 308px; }

/* Climate bands (DEC-0053): the configured acceptable range shown beside a measure's name, and the
   alarm state a reading takes when it — or the headline value — is out of band. Kept in step with
   the Operator overview and Production table, which flag out-of-band readings in the same colour. */
.climate-band-label { margin-left: 8px; font-size: 11.5px; letter-spacing: .04em; color: var(--color-text-muted); }
.climate-band-label.is-alarm { color: var(--color-alarm); font-weight: 600; }
/* Environment tab: the measure name ("Temperature") and its band range read as the plot's heading,
   so they carry more than the 11.6px .lbl default — the name a touch larger than its range. */
.climate-metric-head .climate-key .lbl { font-size: 15.4px; letter-spacing: .08em; }
.climate-metric-head .climate-band-label { font-size: 13px; }
.climate-value.is-alarm { color: var(--color-alarm); }

@media (max-width: 900px) { .climate-split { grid-template-columns: 1fr; } }
@media (max-width: 700px) { .climate-plot-tall { height: 220px; } }

/* ── States: empty, error, skeleton, stale ───────────────────────────────── */

.state-empty { padding: 28.6px 17.6px; text-align: center; font-size: 14.3px; color: var(--color-text-muted); }
.banner {
  border: 1px solid var(--color-accent-900); background: var(--color-accent-100);
  padding: 9.9px 13.2px; font-size: 14.3px; color: var(--color-text);
}
.banner-alarm { border-color: var(--color-alarm); background: var(--color-alarm-tint); }
.field-error { font-size: 13.2px; color: var(--color-accent-900); margin-top: 4.4px; }
.input.is-invalid, .form-control.is-invalid { border-left: 2px solid var(--color-accent-900); }
.skeleton { background: var(--color-track); height: 1em; }

/* ── Operator terminal ───────────────────────────────────────────────────── */

.terminal { display: flex; flex-direction: column; min-height: 100vh; background: var(--color-bg); }
.terminal-bar {
  height: 61.6px; flex: none;
  display: flex; align-items: center; gap: 17.6px; padding: 0 24.2px;
  background: var(--color-accent-900); color: #fff;
}
.terminal-bar a { color: #fff; text-decoration: none; }
.terminal-bar :focus-visible { outline: 2px solid #fff; outline-offset: -2px; }
.terminal-bar .app-brand-name { font-size: 20.9px; }
.terminal-bar .app-brand-sub { font-size: 11px; letter-spacing: .3em; }
.terminal-clock { font-family: var(--font-mono); font-variant-numeric: tabular-nums; font-size: 13.2px; color: var(--color-accent-300); }
.terminal-content { padding: 24.2px; display: flex; flex-direction: column; gap: 19.8px; flex: 1; }

/* Shop-floor tablets: every control is a 48.4px target. */
.terminal .form-control, .terminal .input, .terminal select.form-control { min-height: 48.4px; font-size: 16.5px; }
.terminal .btn { min-height: 48.4px; padding: var(--space-2) var(--space-6); font-size: 16.5px; }
.terminal .btn-row { width: 44px; height: 44px; }
.terminal .table td { padding: 13.2px 17.6px; }
.terminal .table a { font-family: var(--font-heading); font-weight: 600; font-size: 14.3px; letter-spacing: .06em; text-transform: uppercase; }
/* The tab bar was the one control left at desktop size on a layout whose whole point is that
   everything is thumb-sized. 17.4px, with the padding grown to keep the target about a finger
   tall. Still an override now the base has grown from 11.6px to 14px: this one is sized for a
   thumb rather than for reading, so it does not follow the desktop figure. */
.terminal .tabbar a { font-size: 17.4px; padding: 13.2px 19.8px; }

/* ── Auth (login 2a / 2b) ────────────────────────────────────────────────── */

.auth-split { display: grid; grid-template-columns: 1fr 1fr; min-height: 100vh; }
.auth-brandside {
  background: var(--color-accent-900); color: #fff;
  padding: 37.4px 41.8px; display: flex; flex-direction: column;
}
.auth-brandside :focus-visible { outline: 2px solid #fff; outline-offset: -2px; }
.auth-brandside .app-brand-name { font-size: 28.6px; }
.auth-brandside .app-brand-sub { font-size: 13.2px; letter-spacing: .38em; }
.auth-pitch {
  margin-top: 37.4px; border-top: 1px solid rgba(255, 255, 255, .16); padding-top: 24.2px;
}
.auth-pitch .lbl { color: var(--color-accent-300); }
.auth-pitch h2 { font-size: 37.4px; line-height: 1.06; max-width: 14em; margin: 6.6px 0 0; color: #fff; }

.auth-formside {
  background: var(--color-bg);
  display: flex; align-items: center; justify-content: center;
  padding: 37.4px 41.8px;
}
.auth-form { width: 374px; max-width: 100%; display: flex; flex-direction: column; }
/* Registration carries a full company address plus an account — 374px would make it a
   fourteen-field column. Same language, wider measure, two-up rows. */
.auth-form.is-wide { width: 616px; }
.auth-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 0 15.4px; }
.auth-grid .field { margin-bottom: 15.4px; }
@media (max-width: 640px) { .auth-grid { grid-template-columns: 1fr; } }
.auth-form h1 { font-size: 35.2px; margin: 2px 0 24.2px; }
.auth-form .field { margin-bottom: 15.4px; }
.auth-form .field > label { font-family: var(--font-heading); font-weight: 600; font-size: 11.6px; letter-spacing: .14em; text-transform: uppercase; color: var(--color-text-muted); margin-bottom: 5.5px; }
.auth-form .input, .auth-form .form-control { min-height: 44px; }
.auth-label-row { display: flex; align-items: baseline; justify-content: space-between; }
.auth-label-row a { font-family: var(--font-body); font-weight: 400; font-size: 13.2px; letter-spacing: 0; text-transform: none; }
.auth-pw { position: relative; }
.auth-pw .form-control, .auth-pw .input { padding-right: 41.8px; }
.auth-reveal {
  position: absolute; right: 2px; top: 2px; width: 39.6px; height: 39.6px;
  display: inline-flex; align-items: center; justify-content: center;
  background: transparent; border: 0; cursor: pointer; color: var(--color-text-muted);
}
.auth-reveal:hover { color: var(--color-accent); }
.auth-links {
  margin-top: 22px; padding-top: 17.6px; border-top: 1px solid var(--color-divider);
  display: flex; flex-direction: column; gap: 6.6px;
  font-size: 14.3px; color: var(--color-text-muted);
}
.auth-build { font-family: var(--font-mono); font-size: 12.1px; color: var(--color-text-faint); margin-top: 24.2px; }

/* Password rules. No red — unmet rules are muted, met rules take the accent and a tick. */
.pw-rules { list-style: none; padding: 0; margin: 2px 0 19.8px; display: flex; flex-wrap: wrap; gap: 4.4px 15.4px; }
.pw-rules li { font-size: 13.2px; color: var(--color-text-muted); }
.pw-rules li::before { content: "○ "; color: var(--color-text-faint); }
.pw-rules li.text-success { color: var(--color-accent-700); }
.pw-rules li.text-success::before { content: "● "; color: var(--color-accent); }
.pw-rules li.text-danger { color: var(--color-text-muted); }

/* 2b — single plate */
.auth-plate-wrap {
  min-height: 100vh; background: var(--color-bg);
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  padding: 37.4px;
}
.auth-plate-brand { text-align: center; margin-bottom: 26.4px; }
.auth-plate-brand .app-brand-name { font-size: 26.4px; color: var(--color-text); }
.auth-plate-brand .app-brand-sub { font-size: 12.1px; letter-spacing: .38em; color: var(--color-accent-700); }
.auth-plate { width: 396px; max-width: 100%; padding: 28.6px 28.6px 26.4px; background: transparent; }
.auth-plate h1 { font-size: 30.8px; margin: 2px 0 22px; }
.auth-plate-eyebrow { font-family: var(--font-mono); font-size: 12.1px; letter-spacing: .1em; color: var(--color-text-faint); text-transform: uppercase; }

/* ── Responsive ──────────────────────────────────────────────────────────── */

@media (max-width: 1200px) {
  .kpi-strip { grid-template-columns: repeat(2, 1fr); }
  .band-2, .band-3 { grid-template-columns: 1fr !important; }
}
@media (max-width: 900px) {
  .app-shell { grid-template-columns: 61.6px 1fr; }
  .app-shell .app-brand-name, .app-shell .app-brand-sub,
  .app-shell .app-nav-section, .app-shell .app-nav-item span,
  .app-shell .app-site-label, .app-shell .app-site-name { display: none; }
  .app-shell .app-nav-item { justify-content: center; padding: 9.9px 0; border-left-width: 0; }
  .app-shell .app-nav-item.is-active { border-left: 3px solid #fff; padding-left: 0; }
  /* 2a collapses to 2b: the steel panel is dropped, not stacked — a user on a
     line tablet should not scroll past marketing to reach the password field. */
  .auth-split { grid-template-columns: 1fr; }
  .auth-brandside { display: none; }
  .app-content, .terminal-content { padding: 15.4px; }
  .kpi-strip { grid-template-columns: repeat(2, 1fr); }
}
@media (max-width: 900px) {
  /* Touch targets on tablet-facing controls. */
  .industry .form-control, .industry .input, .industry .btn { min-height: 48.4px; }
  .auth-form .input, .auth-form .form-control { min-height: 48.4px; }
}
@media (max-width: 640px) {
  .kpi-strip { grid-template-columns: 1fr; }
  .page-head { align-items: flex-start; flex-direction: column; }
}

/* Tables scroll inside their panel rather than reflowing — the mono columns must stay
   aligned. `auto` means the scrollbar only appears when the content genuinely does not fit. */
.table-scroll { overflow-x: auto; }

/* DataTables builds its own Bootstrap grid around the table, and those rows carry the default
   -15px gutter. Inside our panel that makes the wrapper 30px wider than its container, so every
   DataTables page showed a horizontal scrollbar regardless of how wide the columns actually
   were. The panel owns the padding here; the injected grid should not add its own. */
.industry .dataTables_wrapper > .row { margin-left: 0; margin-right: 0; }
.industry .dataTables_wrapper > .row > [class*="col-"] { padding-left: 0; padding-right: 0; }

/* Views nest Bootstrap containers inside .app-content, which already provides the 24.2px page
   gutter. The doubled padding (container-fluid 16.5px + card-body 22px on each side) pushed
   otherwise-comfortable tables past the panel edge and produced a horizontal scrollbar on
   pages that had no business scrolling. Neutralise the inner gutters; the page frame owns
   spacing now. */
.app-content > .container-fluid,
.app-content .container-fluid,
.terminal-content > .container-fluid { padding-left: 0; padding-right: 0; max-width: none; }
.app-content .card-body { padding: 0; }
.app-content > .container-fluid > .row,
.app-content .card-body > .row { margin-left: 0; margin-right: 0; }

/* Label/field rows: one column width for every label so inputs start on a common axis.
   The previous per-row `d-flex justify-content-between` let each label size itself, so the
   fields stepped in and out down the form. */
.industry .form-group .d-flex > label.form-label,
.industry .form-group .d-flex > label {
  flex: 0 0 165px; width: 165px; margin: 0; padding-right: 13.2px;
  text-align: right;
  font-family: var(--font-heading); font-weight: 600; font-size: 11.6px;
  letter-spacing: .14em; text-transform: uppercase; color: var(--color-text-muted);
}
.industry .form-group .d-flex > .form-control,
.industry .form-group .d-flex > select.form-control { flex: 1 1 auto; min-width: 0; }

@media (max-width: 640px) {
  .industry .form-group .d-flex > label.form-label,
  .industry .form-group .d-flex > label { flex-basis: 100%; width: 100%; text-align: left; padding: 0 0 4.4px; }
  .industry .form-group .d-flex { flex-wrap: wrap; }
}

/* A form's primary action sits on its own row at the end, right-aligned — not stretched
   across the width of a section header. */
.form-actions {
  display: flex; justify-content: flex-end; gap: 11px;
  margin-top: 19.8px; padding-top: 15.4px; border-top: 1px solid var(--color-divider);
}
.form-actions .btn { min-width: 132px; }

/* Read-only fields read as inset, not editable. */
.industry .form-control[readonly], .industry .form-control:disabled {
  background: var(--color-surface); color: var(--color-text-muted);
}

/* Nothing bounces in a monitoring tool. */
@media (prefers-reduced-motion: reduce) {
  .industry *, .industry *::before, .industry *::after {
    transition-duration: 0.01ms !important; animation-duration: 0.01ms !important;
  }
}


/* ── Registration marks on every framed box ──────────────────────────────────
   The signature of the Industry system: a "+" crosshair at each corner of a framed
   object. <panel> emits real .corner elements; this draws the same mark purely in CSS
   for the many existing .card containers, so no view has to be restructured to get it.
   Uses one overlay with eight 1px gradient strips (two per corner) because an element
   only has ::before and ::after to spend. */
.industry .card,
.industry .panel-marked {
  position: relative;
}
.industry .card::after,
.industry .panel-marked::after {
  content: "";
  position: absolute;
  inset: -6px;
  pointer-events: none;
  z-index: 1;
  --mark: color-mix(in srgb, var(--color-text) 55%, transparent);
  background-image:
    linear-gradient(var(--mark), var(--mark)), linear-gradient(var(--mark), var(--mark)),
    linear-gradient(var(--mark), var(--mark)), linear-gradient(var(--mark), var(--mark)),
    linear-gradient(var(--mark), var(--mark)), linear-gradient(var(--mark), var(--mark)),
    linear-gradient(var(--mark), var(--mark)), linear-gradient(var(--mark), var(--mark));
  background-repeat: no-repeat;
  background-size:
    12.1px 1px, 1px 12.1px,   /* top-left    */
    12.1px 1px, 1px 12.1px,   /* top-right   */
    12.1px 1px, 1px 12.1px,   /* bottom-left */
    12.1px 1px, 1px 12.1px;   /* bottom-right*/
  background-position:
    left 0.5px top 6.1px,  left 6.1px top 0.5px,      /* top-left     */
    right 0.5px top 6.1px, right 6.1px top 0.5px,     /* top-right    */
    left 0.5px bottom 6.1px,  left 6.1px bottom 0.5px,   /* bottom-left  */
    right 0.5px bottom 6.1px, right 6.1px bottom 0.5px;  /* bottom-right */
}

/* A card nested inside a panel would double the marks — the system reserves them for
   top-level framed objects. */
.industry .blueprint .card::after,
.industry .card .card::after { content: none; }

/* No box, no marks. Bootstrap's .border-0 cancels the border but not this ::after overlay, so
   legacy `card o-hidden border-0` wrappers were emitting four crosshairs around nothing — the
   "stray +" that show up on pages carrying pre-redesign markup. Registration marks register a
   frame; without one they are just noise. */
.industry .card.border-0::after { content: none; }

/* Marks are drawn 6.6px outside the box, so an ancestor that clips would eat them. */
.industry .card, .industry .panel-marked { overflow: visible; }

/* ── Configure section: no frames, no marks ──────────────────────────────────
   Lines / Machines / Users / Parts / Company & Settings are lists and forms. A framed box
   around a table adds a second border immediately inside the table's own, and the registration
   marks then sit around content that has no reason to read as an instrument.

   It also removes a real defect: several of these pages wrap content in Bootstrap's
   `.card border-0`, which cancels the border but NOT the CSS-drawn marks — leaving four "+"
   crosshairs floating in empty space with no box to register. Killing the marks by section is
   what fixes those, not just the intentional frames.

   Scoped by section rather than per view so pages added to Configure later inherit it. Modals on
   these pages use .modal-content, not .card, so their framing is unaffected. */
.industry [data-section="configure"] .card::after,
.industry [data-section="configure"] .panel-marked::after { content: none; }

.industry [data-section="configure"] .blueprint > .corner { display: none; }

.industry [data-section="configure"] .blueprint,
.industry [data-section="configure"] .card { border: none; }

/* With the frame gone the panel's own padding only pushes the table away from the page's
   left edge, breaking alignment with the page title above it. */
.industry [data-section="configure"] .blueprint.panel-pad { padding-left: 0; padding-right: 0; }

/* ── User management ─────────────────────────────────────────────────────────
   Roles are a set, not a ladder — a person can be a manager who also runs the terminal — so the
   control is a row of checkboxes rather than a select. Laid out as wrapping chips so three roles
   read as three choices at a glance instead of a stacked list. */
.industry .role-choices { display: flex; flex-wrap: wrap; gap: 8.8px; }

.industry .role-choice {
  display: flex; align-items: center; gap: 6.6px; margin: 0;
  padding: 6.6px 11px; border: 1px solid var(--color-divider);
  background: #fff; cursor: pointer; font-size: 14.3px;
}

.industry .role-choice input { margin: 0; }

.industry .role-choice:has(input:checked) {
  border-color: var(--color-accent);
  background: rgba(89, 128, 166, .08);
}

/* A soft-deleted account stays listed so its email can be released by a restore; it reads as
   present-but-inactive rather than being styled like an error. */
.industry tr.is-deleted td { color: var(--color-text-muted); }

/* Company picker: a search box over a server-backed result list, so it behaves the same whether
   the instance has eight companies or eight thousand. */
.industry .company-picker { display: flex; align-items: center; gap: 13.2px; }

.industry .company-picker-control { position: relative; flex: 1; max-width: 520px; }

.industry .company-picker-results {
  position: absolute; z-index: 30; left: 0; right: 0; top: calc(100% + 2px);
  max-height: 320px; overflow-y: auto;
  background: #fff; border: 1px solid var(--color-divider);
  box-shadow: 0 6px 18px rgba(0, 0, 0, .10);
}

.industry .company-picker-option {
  display: flex; align-items: baseline; gap: 8.8px; width: 100%;
  padding: 8.8px 13.2px; border: 0; background: none; text-align: left;
  font-size: 14.3px; color: var(--color-text);
}

.industry .company-picker-option:hover { background: rgba(89, 128, 166, .10); }

.industry .company-picker-location { font-size: 12px; color: var(--color-text-muted); }

.industry .company-picker-more {
  padding: 8.8px 13.2px; font-size: 12px; color: var(--color-text-muted);
  border-top: 1px solid var(--color-divider);
}

/* Each user-management section is its own table, so auto layout sized every list's columns to
   its own rows and the three sets of headings sat at three different x positions. Fixed layout
   makes the shared <colgroup> authoritative, which is what puts them in one grid; long emails
   then have to be allowed to wrap, since a fixed cell cannot widen to fit one. */
.industry .table-users { table-layout: fixed; }
.industry .table-users td { overflow-wrap: anywhere; }
