/* Erektor Return System - shared shell.
   Single source for tokens, the facility rail, topbar, summary strip, tables
   and controls. Every page links this; page-specific rules stay in that page
   and therefore still win the cascade. See DESIGN.md. */

/* Brand type, self-hosted so a facility console never waits on a third-party
   font host: Oswald caps for headings, controls and the wordmark; Inter for
   body. Variable, latin subset. */
@font-face {
  font-family: "Oswald"; font-style: normal; font-weight: 400 700; font-display: swap;
  src: url("fonts/oswald-latin.woff2") format("woff2");
}
@font-face {
  font-family: "Inter"; font-style: normal; font-weight: 400 800; font-display: swap;
  src: url("fonts/inter-latin.woff2") format("woff2");
}

:root {
  --font-display: "Oswald", "Arial Narrow", sans-serif;
  --font-ui: "Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", Arial, sans-serif;
  --font-mono: ui-monospace, "SF Mono", "JetBrains Mono", "Cascadia Code", Menlo, Consolas, monospace;
  --type-body: 14px;
  --type-meta: 12px;
  --type-label: 11px;
  --leading-body: 1.45;
  --leading-tight: 1.08;

  /* ---- Primitives. The raw palette; nothing outside :root names these. ---- */
  --neutral-0: #ffffff;
  --neutral-50: #fafafa;
  --neutral-100: #f5f5f5;
  --neutral-150: #efefef;
  --neutral-200: #e8e8e8;
  --neutral-250: #d9d9d9;
  --neutral-300: #d0d0d0;
  --neutral-400: #b0b0b0;
  --neutral-450: #a0a0a0;
  --neutral-500: #8a8a8a;
  --neutral-600: #696969;
  --neutral-700: #555555;
  --neutral-750: #474747;
  --neutral-800: #3c3c3c;
  --neutral-900: #2c2c2c;
  --neutral-950: #1c1c1c;
  --neutral-1000: #000000;
  /* Status hues are erektor.support's light palette, so a chip or a chart
     reads the same on both sites. */
  --green-100: #e6f4ea;
  --green-200: #b5d6c1;
  --green-700: #137334;
  --amber-100: #fdf3e0;
  --amber-300: #e9cc95;
  --amber-700: #976013;
  --red-50: #fdf3f0;
  --red-100: #f9e6e6;
  --red-150: #f3d2d2;
  --red-200: #e5aaaa;
  --red-500: #c0392b;
  --red-600: #c70000;
  --red-900: #8b0000;
  --blue-50: #e6eef8;
  --blue-800: #1f5f9e;
  --steel-500: #5a6a6f;
  --steel-600: #4a5a5f;
  --night-100: #d6dde6;
  --night-800: #0d1624;
  --night-900: #04070c;

  /* ---- Semantic. What a colour is for. ---- */
  --surface: var(--neutral-0);
  --panel: var(--neutral-50);
  --paper: var(--neutral-100);
  --panel-header: var(--neutral-150);
  --line: var(--neutral-250);
  --line-strong: var(--neutral-400);
  --ink: var(--neutral-900);
  --ink-pressed: var(--neutral-950);
  --ink-strong: var(--neutral-1000);
  --on-ink: var(--neutral-0);
  /* De-emphasised text. It clears 4.5:1 on every light surface the console
     paints (4.7:1 header fill, 5.0:1 paper, 5.3:1 panel, 5.5:1 surface). */
  --muted: var(--neutral-600);

  /* The dark chrome: masthead, facility bar and the sign-in rail. Every
     text tone clears 4.5:1 on --chrome-raised, the lighter of the two. */
  --chrome: var(--neutral-900);
  --chrome-raised: var(--neutral-800);
  --chrome-hover: var(--neutral-750);
  --chrome-line: var(--neutral-700);
  --chrome-line-hover: var(--neutral-450);
  --chrome-edge: var(--neutral-950);
  --chrome-selected: var(--neutral-100);
  --chrome-text: var(--neutral-0);
  --chrome-text-high: var(--neutral-200);
  --chrome-text-body: var(--neutral-300);
  --chrome-text-muted: var(--neutral-400);
  --chrome-text-disabled: var(--neutral-500);

  /* The brand red is the wordmark and the guide's accent, never a status:
     a healthy state reading in the same red as a hold made the two look
     alike at a glance. Confirmed is green; red is critical only. */
  --brand: var(--red-500);
  --brand-deep: var(--red-900);
  --brand-grey: var(--neutral-500);
  /* The keyboard focus ring, everywhere. */
  --focus: var(--blue-800);
  /* Three severities and a neutral fact. Each severity has a fill tone, a
     tint, an on-tint text tone and a rule. */
  --confirmed: var(--green-700);
  --confirmed-soft: var(--green-100);
  --confirmed-text: var(--green-700);
  --confirmed-line: var(--green-200);
  --attention: var(--amber-700);
  --attention-soft: var(--amber-100);
  --attention-text: var(--amber-700);
  --attention-line: var(--amber-300);
  --attention-edge: var(--amber-300);
  --critical: var(--red-600);
  --critical-soft: var(--red-100);
  --critical-softer: var(--red-50);
  --critical-soft-hover: var(--red-150);
  --critical-text: var(--red-600);
  --critical-line: var(--red-200);
  --information: var(--blue-800);
  --information-soft: var(--blue-50);

  /* ---- Component. Where one part of the console takes its colours. ---- */
  --masthead-bg: var(--chrome);
  --subnav-bg: var(--chrome-raised);
  --nav-text: var(--chrome-text-body);
  --nav-hover-bg: var(--chrome-hover);
  --nav-active-bg: var(--chrome-selected);
  --field-dark-bg: var(--chrome-raised);
  --field-dark-line: var(--chrome-line);
  --chart-track: var(--neutral-150);
  --chart-bar: var(--information);
  --globe-space-inner: var(--night-800);
  --globe-space-outer: var(--night-900);
  --globe-fallback-text: var(--night-100);
  --belt-top: var(--steel-500);
  --belt-bottom: var(--neutral-750);
  --belt-rail-top: var(--steel-600);
  --belt-rail-bottom: var(--neutral-800);

  --radius: 8px;
  --radius-control: 4px;
  --radius-surface: 12px;
  /* erektor.support's raised card: a hairline highlight over a soft drop. */
  --shadow-surface: 0 1px 0 rgba(255, 255, 255, .6), 0 20px 40px -26px rgba(15, 30, 40, .35);
  --ease-standard: cubic-bezier(.2, .7, .2, 1);
  --motion-fast: 160ms;
  --motion-standard: 240ms;
}
* { box-sizing: border-box; }
body {
  margin: 0;
  background: var(--paper);
  color: var(--ink);
  font: var(--type-body)/var(--leading-body) var(--font-ui);
  -webkit-font-smoothing: antialiased;
  overflow-x: hidden;
}
button { font: inherit; cursor: pointer; }
/* No double-tap zoom wait on a tablet. */
a, button, input, select, label { touch-action: manipulation; }
.visually-hidden {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  overflow: hidden;
  white-space: nowrap;
  clip-path: inset(50%);
}
/* First stop for a keyboard: past the masthead and the facility bar. */
.skip-link {
  position: absolute;
  top: 8px;
  left: 8px;
  z-index: 100;
  padding: 10px 14px;
  border-radius: var(--radius-control);
  background: var(--surface);
  color: var(--ink);
  font-weight: 800;
  transform: translateY(-200%);
}
.skip-link:focus { transform: none; }
main:focus { outline: none; }
a, button, .status-chip, .action, .signout, .support-link {
  transition:
    background-color var(--motion-fast) var(--ease-standard),
    border-color var(--motion-fast) var(--ease-standard),
    color var(--motion-fast) var(--ease-standard),
    box-shadow var(--motion-fast) var(--ease-standard),
    transform var(--motion-fast) var(--ease-standard);
}
button:focus-visible, a:focus-visible { outline: 3px solid var(--focus); outline-offset: 3px; }
.shell { min-height: 100vh; }
.brand {
  display: flex;
  align-items: center;
  /* The full wordmark sets on one line inside the 246px rail, so it runs
     smaller than the rest of the shell; nowrap keeps it a single lockup.
     The name is one flex item, so the gap below separates the mark from the
     name only and every word is parted by the same single space. */
  flex-wrap: nowrap;
  white-space: nowrap;
  gap: 11px;
  font-family: var(--font-display);
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: .06em;
  font-size: var(--type-label);
  line-height: 1.15;
}
.brand-mark {
  width: 25px;
  height: 25px;
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: 3px;
}
.brand-mark i { display: block; background: var(--brand); }
.brand-mark i:nth-child(2), .brand-mark i:nth-child(3) { background: var(--brand-grey); }
/* Support is a destination, not a nav item: it leaves the console, so it
   travels beside the wordmark rather than inside the rail's navigation.
   The rail is only 246px wide, so the link takes its own line whenever it
   cannot share one with the full lockup. */
.brand-row {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 10px;
}
/* Support leaves the console, so it is a link, not a button. */
.support-link {
  display: inline-flex;
  align-items: center;
  min-height: 44px;
  padding: 0 4px;
  color: var(--chrome-text-body);
  font-size: 13px;
  font-weight: 700;
  line-height: 1.2;
  white-space: nowrap;
  text-decoration: underline;
  text-underline-offset: 4px;
}
.support-link:hover { color: var(--chrome-text); }
.facility { border-top: 1px solid var(--chrome-line); border-bottom: 1px solid var(--chrome-line); padding: 16px 0; }
.facility strong, .facility span { display: block; }
.facility span {
  color: var(--chrome-text-body);
  font-size: var(--type-label);
  line-height: 1.2;
  letter-spacing: .1em;
  text-transform: uppercase;
  margin-bottom: 5px;
}
/* The facility block becomes a picker when the account has more than one
   facility (assets/ers-session.js). It keeps the rail's type: a small caps
   label over the facility name, with a chevron marking it as a choice. */
.facility label {
  display: block;
  color: var(--chrome-text-body);
  font-size: var(--type-label);
  line-height: 1.2;
  letter-spacing: .1em;
  text-transform: uppercase;
  font-weight: 700;
  margin-bottom: 7px;
}
.facility-picker {
  appearance: none;
  -webkit-appearance: none;
  width: 100%;
  min-height: 44px;
  padding: 0 34px 0 12px;
  border: 1px solid var(--field-dark-line);
  border-radius: var(--radius-control);
  background-color: var(--field-dark-bg);
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='8' viewBox='0 0 12 8'%3E%3Cpath d='M1 1.5l5 5 5-5' fill='none' stroke='%23e0e0e0' stroke-width='1.8' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right 12px center;
  color: var(--chrome-text);
  font: inherit;
  font-size: 14px;
  font-weight: 700;
  text-overflow: ellipsis;
  cursor: pointer;
}
.facility-picker:hover { border-color: var(--chrome-line-hover); }
.facility-picker:focus-visible { outline: 3px solid var(--focus); outline-offset: 2px; }
.facility-picker option { background: var(--field-dark-bg); color: var(--chrome-text); }
.facility-picker option:disabled { color: var(--chrome-text-disabled); }
/* ---------------------------------------------------------------------------
   The masthead. One dark band across the top of every console page: the
   wordmark (a link home), then the page's title, the facility the
   console is scoped to, and the signed-in account (assets/ers-session.js).
   -------------------------------------------------------------------------*/
/* The masthead and the facility bar stay pinned while the page scrolls,
   as one block so the bar follows the masthead whatever height it wraps to.
   Under the sheet backdrop (20), over everything on the page. */
.site-head { position: sticky; top: 0; z-index: 15; }
.masthead {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr) auto auto auto;
  align-items: center;
  gap: 24px;
  min-height: 72px;
  padding: 0 24px;
  background: var(--masthead-bg);
  border-bottom: 1px solid var(--chrome-edge);
  color: var(--chrome-text);
}
.masthead .brand { gap: 12px; color: inherit; text-decoration: none; }
.masthead .brand-mark { width: 28px; height: 28px; flex-shrink: 0; }
.masthead .brand-name { font-size: 13px; letter-spacing: .16em; }
.masthead .brand-name span { display: block; margin-top: 4px; color: var(--chrome-text-muted); font-size: 10px; font-weight: 700; letter-spacing: .2em; }
.masthead h1 { min-width: 0; padding-left: 24px; border-left: 1px solid var(--chrome-line); font-size: clamp(20px, 1.8vw, 26px); letter-spacing: -.02em; }

.masthead .facility {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 0;
  border: 0;
}
.masthead .facility span, .masthead .facility label {
  margin: 0;
  font-size: 11px;
  letter-spacing: .14em;
  color: var(--chrome-text-muted);
  white-space: nowrap;
}
.masthead .facility strong { font-size: 15px; line-height: 1.25; }
.masthead .facility-picker { width: 260px; min-height: 44px; }

.masthead .account { gap: 10px; min-height: 44px; padding-left: 20px; border-left: 1px solid var(--chrome-line); }
.masthead .account-avatar { background: var(--chrome-selected); color: var(--ink); }
.masthead .account-user strong { font-size: 14px; line-height: 1.15; }
.masthead .account-user span { font-size: 12px; color: var(--chrome-text-body); line-height: 1.25; }
.masthead .signout {
  min-height: 44px;
  padding: 0 16px;
  border: 1px solid var(--field-dark-line);
  border-radius: var(--radius-control);
  background: transparent;
  color: var(--chrome-text);
  font-size: 13px;
  font-weight: 700;
  line-height: 1;
}
.masthead .signout:hover { border-color: var(--chrome-line-hover); background: var(--chrome-raised); }
.masthead .signout:focus-visible { outline: 3px solid var(--focus); outline-offset: 2px; }

/* ---------------------------------------------------------------------------
   The facility bar. Once a facility is picked, its pages sit in one row
   under the masthead; Home has none. The current page is the one light tab,
   and a count rides the tab's end.
   -------------------------------------------------------------------------*/
.subnav {
  display: flex;
  gap: 4px;
  padding: 8px 24px;
  background: var(--chrome-raised);
  overflow-x: auto;
  scrollbar-width: none;
}
.subnav::-webkit-scrollbar { display: none; }
@media (max-width: 980px) {
  .subnav { -webkit-mask-image: linear-gradient(to right, #000 calc(100% - 40px), transparent); mask-image: linear-gradient(to right, #000 calc(100% - 40px), transparent); }
}
nav a {
  flex: 0 0 auto;
  display: flex;
  align-items: center;
  gap: 10px;
  min-height: 44px;
  padding: 0 14px;
  white-space: nowrap;
  border-radius: 6px;
  color: var(--chrome-text-body);
  font-size: 14px;
  line-height: 1.2;
  text-decoration: none;
}
nav a::before {
  content: "";
  width: 18px;
  height: 18px;
  flex-shrink: 0;
  background: currentColor;
  -webkit-mask: var(--icon) center / contain no-repeat;
  mask: var(--icon) center / contain no-repeat;
  opacity: .85;
}
nav a:hover { background: var(--nav-hover-bg); color: var(--chrome-text); }
nav a.active { background: var(--chrome-selected); color: var(--ink); font-weight: 700; }
nav a.active::before { opacity: 1; }
nav b {
  margin-left: 2px;
  min-width: 22px;
  height: 20px;
  padding: 0 6px;
  border-radius: 10px;
  background: var(--critical);
  color: var(--on-ink);
  font-size: 11px;
  font-weight: 800;
  display: inline-flex;
  align-items: center;
  justify-content: center;
}
nav a[href="index.html"] { --icon: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M3 12h4l3-8 4 16 3-8h4'/%3E%3C/svg%3E"); }
nav a[href="leg-registry.html"] { --icon: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M8 6h13M8 12h13M8 18h13M3 6h.01M3 12h.01M3 18h.01'/%3E%3C/svg%3E"); }
nav a[href="battery-packs.html"] { --icon: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Crect x='2' y='7' width='17' height='10' rx='2'/%3E%3Cpath d='M22 11v2M6 10v4M10 10v4'/%3E%3C/svg%3E"); }
nav a[href="controllers.html"] { --icon: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Crect x='6' y='6' width='12' height='12' rx='1.5'/%3E%3Crect x='9.5' y='9.5' width='5' height='5' rx='.5'/%3E%3Cpath d='M9.5 2v4M14.5 2v4M9.5 18v4M14.5 18v4M2 9.5h4M2 14.5h4M18 9.5h4M18 14.5h4'/%3E%3C/svg%3E"); }
nav a[href="maintenance.html"] { --icon: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M14.7 6.3a4 4 0 0 0-5.4 5.4L3 18l3 3 6.3-6.3a4 4 0 0 0 5.4-5.4l-2.6 2.6-2.4-.6-.6-2.4z'/%3E%3C/svg%3E"); }
main { overflow: hidden; }
.warehouse-label {
  position: absolute;
  top: 16px;
  left: 18px;
  z-index: 1;
  color: rgba(44,44,44,.72);
  text-transform: uppercase;
  letter-spacing: .14em;
  font-size: var(--type-label);
  line-height: 1.2;
  font-weight: 700;
}
/* The page's toolbar: the date and the page's own controls, set as the
   first row of the content rather than a second bar under the masthead. */
.topbar {
  max-width: 1600px;
  margin: 0 auto;
  padding: 22px 32px 0;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 20px;
}
h1, h2, p { margin: 0; }
h1, h2, h3 { font-family: var(--font-display); text-transform: uppercase; font-weight: 700; }
h1 {
  font-size: clamp(24px, 3vw, 36px);
  letter-spacing: 0;
  line-height: var(--leading-tight);
  text-wrap: balance;
}
.date { color: var(--muted); font-size: var(--type-meta); line-height: 1.3; }
.actions { display: flex; align-items: center; gap: 12px; }
.actions > * { min-width: 0; }
/* Topbar controls keep their label on one line; a squeezed topbar wraps
   whole controls rather than breaking "Facility online" mid-chip. */
.actions { flex-wrap: wrap; justify-content: flex-end; }
.actions .status-chip, .actions .action, .actions .filter, .actions .signout { white-space: nowrap; }
.status-chip, .action, .signout, .filter {
  border: 1px solid var(--line);
  background: var(--panel);
  color: var(--ink);
  padding: 9px 11px;
  border-radius: 4px;
  font-weight: 700;
}
.action, .signout, .filter { font-family: var(--font-display); font-weight: 500; text-transform: uppercase; letter-spacing: .04em; }
/* Operated from a floor workstation, often with a glove on: every console
   control clears 44px, not only the ones inside the exception sheet. */
.action, .signout, .filter {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-height: 44px;
}
.filter[aria-pressed="true"] { background: var(--ink); border-color: var(--ink); color: var(--on-ink); }
/* On a tint, type takes the tint's own text tone. --confirmed is the fill
   tone and measured 4.49:1 here, which is the saturated-tone-as-type mistake
   DESIGN.md names by hand. */
.status-chip { color: var(--confirmed-text); border-color: var(--confirmed-line); background: var(--confirmed-soft); }
.action { background: var(--ink); border-color: var(--ink); color: var(--on-ink); }
.storage-warning {
  max-width: 30ch;
  color: var(--critical-text);
  font-size: var(--type-label);
  line-height: 1.25;
}

/* Signed-in identity. Rendered into the topbar by assets/ers-session.js, so it
   is styled with the shell rather than by any one page. */
.account {
  display: flex;
  align-items: center;
  gap: 10px;
  padding-left: 12px;
  border-left: 1px solid var(--line);
}
.account-user { display: grid; line-height: 1.2; }
.account-user strong { min-width: 0; overflow-wrap: anywhere; }
.account-user strong { font-weight: 800; }
.account-user small { color: var(--muted); font-size: var(--type-label); }
.signout:hover, .filter:hover { border-color: var(--ink); }
/* ---------------------------------------------------------------------------
   Topbar controls. One button system for the whole bar: every control is the
   same 44px glove target with the same padding and type, so the row reads as
   one aligned strip. The page's own verb is the single ink button; Guide,
   Reset demo data and Sign out are outlined; the facility's state is a status,
   not a control, so it drops its button shape.
   -------------------------------------------------------------------------*/
.topbar .actions { gap: 8px; }
.topbar .action, .topbar .filter, .topbar .signout {
  min-height: 44px;
  padding: 0 16px;
  gap: 8px;
  border-radius: var(--radius-control);
  font-size: 13px;
  font-weight: 700;
  line-height: 1;
  letter-spacing: 0;
}
.topbar .action:hover { background: var(--ink-pressed); border-color: var(--ink-pressed); }
.topbar .filter, .topbar .signout, .topbar .tour-replay {
  background: var(--surface);
  border-color: var(--line);
  color: var(--ink);
}
.topbar .filter:hover, .topbar .signout:hover, .topbar .tour-replay:hover { border-color: var(--ink); background: var(--surface); }
.topbar .action:focus-visible, .topbar .filter:focus-visible, .topbar .signout:focus-visible {
  outline: 3px solid var(--focus);
  outline-offset: 2px;
}
/* Guide carries a question mark, so the outlined pair reads as help first. */
.topbar .tour-replay::before {
  content: "?";
  width: 18px;
  height: 18px;
  border-radius: 50%;
  border: 1.5px solid currentColor;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  font-size: 11px;
  font-weight: 800;
}
.topbar .status-chip {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  min-height: 44px;
  margin-right: 8px;
  padding: 0;
  border: 0;
  background: none;
  color: var(--confirmed-text);
  font-size: 13px;
  font-weight: 700;
}
.topbar .status-chip::before {
  content: "";
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: var(--confirmed);
  box-shadow: 0 0 0 4px var(--confirmed-soft);
}
.account-avatar {
  width: 36px;
  height: 36px;
  flex-shrink: 0;
  border-radius: 50%;
  background: var(--ink);
  color: var(--on-ink);
  font-size: 12px;
  font-weight: 800;
  letter-spacing: .04em;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  position: relative;
  overflow: hidden;
}
.account-avatar img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
}
/* The name gives way before the controls do (DESIGN.md): below this width the
   avatar, titled with the address, stands for the operator. */
@media (max-width: 1280px) {
  .masthead .account-user { display: none; }
}

.content { padding: 18px 32px 42px; max-width: 1600px; margin: auto; }
.summary {
  display: grid;
  grid-template-columns: 1.35fr repeat(3, 1fr);
  border: 1px solid var(--line);
  border-radius: var(--radius-surface);
  box-shadow: var(--shadow-surface);
  background: var(--panel);
  overflow: clip;
}
.summary > * { padding: 16px 18px; min-height: 93px; border-right: 1px solid var(--line); }
/* Each cell's label, figure and detail share row tracks with its neighbours,
   so a label that wraps to two lines moves the whole row's figures together
   instead of dropping its own figure below the others. */
.summary > * { display: grid; grid-template-rows: subgrid; grid-row: span 3; align-content: start; }
/* A tile is a link to the rows it counts. */
.summary > a { color: inherit; text-decoration: none; }
.summary > a:hover { background: var(--surface); }
.summary > a:hover .label { text-decoration: underline; text-underline-offset: 3px; }
.summary > a:focus-visible { outline: 3px solid var(--focus); outline-offset: -3px; }
.summary .label { align-self: end; }
.summary > :last-child { border: 0; }
.summary strong {
  display: block;
  font-size: 29px;
  letter-spacing: -.04em;
  line-height: var(--leading-tight);
  font-variant-numeric: tabular-nums;
  margin: 14px 0 4px;
}
.summary small { color: var(--muted); font-size: var(--type-meta); line-height: 1.3; }
/* Summary tint modifiers live with the console surface below. */

/* ---------------------------------------------------------------------------
   Console surface.

   Every page in the console is the same object: a topbar, a summary strip, and
   ruled panels holding one table. Those rules lived in six places — inside
   ers-line.css, which is the recovery line and is linked by one page, plus a
   pasted copy in each interior page — so the contrast and colour-blindness
   fixes made on the operations page never reached the registry, the battery
   wall, deployments, maintenance or reconciliation. They live here once.
   ers-line.css keeps only what is actually the line.
   -------------------------------------------------------------------------*/
.summary .label, .panel-title span, .leg-sheet dt {
  color: var(--muted);
  text-transform: uppercase;
  letter-spacing: .1em;
  font-weight: 700;
  font-size: var(--type-label);
  line-height: 1.2;
}
.summary .availability, .summary .confirmed { background: var(--confirmed-soft); }
.summary .confirmed strong { color: var(--confirmed-text); }
.summary .exceptions, .summary .attention { background: var(--attention-soft); }
.summary .exceptions strong, .summary .attention strong { color: var(--attention-text); }
.summary .critical { background: var(--critical-soft); }
.summary > a.critical:hover { background: var(--critical-soft-hover); }
.summary .critical strong { color: var(--critical-text); }
/* A tinted summary cell carries its label and detail in the tint's text tone
   too: --muted is a grey for paper and panel, and it drops to 4.0:1 on the
   critical tint. */
.summary .confirmed .label, .summary .confirmed small { color: var(--confirmed-text); }
.summary .attention .label, .summary .attention small,
.summary .exceptions .label, .summary .exceptions small { color: var(--attention-text); }
.summary .critical .label, .summary .critical small { color: var(--critical-text); }

.line-panel {
  margin-top: 28px;
  border: 1px solid var(--line);
  border-radius: var(--radius-surface);
  box-shadow: var(--shadow-surface);
  background: var(--panel);
  overflow: clip;
}
.panel-head {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  padding: 16px 18px;
  border-bottom: 1px solid var(--ink);
  gap: 18px;
}
/* A 44px control baseline-aligned beside the title pushed the title down
   against every panel that carries only the live tag. It sits at the top of
   the head instead and the title keeps the same inset on every panel. */
.panel-head > .filter { align-self: flex-start; margin-top: -6px; }
.panel-title h2 { font-size: 19px; letter-spacing: 0; line-height: 1.05; text-wrap: balance; margin-bottom: 5px; }
.panel-head p { color: var(--muted); font-size: var(--type-body); line-height: 1.4; max-width: 72ch; }
.live {
  color: var(--muted);
  font-size: var(--type-label);
  line-height: 1.2;
  letter-spacing: .08em;
  text-transform: uppercase;
  font-weight: 700;
  white-space: nowrap;
}

/* The ledger sheet. A console table keeps every column and scrolls, rather
   than dropping the facts an operator needs to act on; body carries
   overflow-x: hidden, so a table without this wrapper is not scrolled at
   narrow widths, it is silently cut off. The wrapper takes focus because a
   scroll region a pointer can reach must also be reachable by keyboard. */
.table-scroll {
  overflow-x: auto;
  -webkit-overflow-scrolling: touch;
  /* Reaching the end of a row must not then drag the page sideways. */
  overscroll-behavior-x: contain;
}
.table-scroll:focus-visible { outline: 3px solid var(--focus); outline-offset: -3px; }
.table-scroll table { min-width: var(--table-min, 660px); }
table { width: 100%; border-collapse: collapse; text-align: left; }
caption {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  overflow: hidden;
  white-space: nowrap;
  clip-path: inset(50%);
}
/* Column headers, the condition detail, the location and the age are all read
   to decide an action, so they carry ink. Rank is set by size, weight and case
   rather than by tone. */
/* The ruled table and its pills follow erektor.support's: a grey caps header
   over a strong rule, light row rules, a grey row on hover, and serials in
   monospace because they are read back over a radio. The header text stays
   --muted rather than the support site's fainter grey, which is 2.8:1. */
th {
  color: var(--muted);
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: .08em;
  font-size: 11.5px;
  line-height: 1.2;
  padding: 11px 14px;
  background: var(--panel-header);
  border-bottom: 1px solid var(--line-strong);
  white-space: nowrap;
}
td { padding: 11px 14px; border-top: 1px solid var(--line); vertical-align: middle; line-height: 1.35; }
tbody tr:first-child td { border-top: 0; }
tbody tr:hover td { background: var(--panel-header); }
td:first-child, td.asset { font-family: var(--font-mono); font-size: 13px; font-weight: 700; white-space: nowrap; }
td.condition strong { display: block; }
td.condition span { color: var(--ink); font-size: var(--type-meta); }
td.location { color: var(--ink); }
/* A deployment's leg serials: one monospace chip per leg, one L/R pair per line. */
.serials { display: grid; grid-template-columns: repeat(2, max-content); gap: 4px 10px; font-family: var(--font-mono); font-size: 12.5px; font-weight: 700; }
/* Search above a panel's rows (assets/ers-table.js). */
.panel-search {
  display: flex;
  align-items: center;
  gap: 14px;
  padding: 12px 18px;
  border-bottom: 1px solid var(--line);
}
.panel-search input {
  width: min(360px, 100%);
  min-height: 44px;
  padding: 0 12px 0 38px;
  background: var(--surface) url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' viewBox='0 0 24 24' fill='none' stroke='%23696969' stroke-width='2.2' stroke-linecap='round'%3E%3Ccircle cx='11' cy='11' r='7'/%3E%3Cpath d='M20 20l-4-4'/%3E%3C/svg%3E") no-repeat 13px center;
  border: 1px solid var(--line);
  border-radius: var(--radius-control);
  color: var(--ink);
  font: inherit;
}
.panel-search input:focus-visible { outline: 3px solid var(--focus); outline-offset: 1px; border-color: var(--ink); }
.panel-search output { color: var(--muted); font-size: var(--type-meta); }
.search-empty { display: flex; align-items: center; gap: 14px; flex-wrap: wrap; }
.search-empty[hidden] { display: none; }
/* A row with a View link opens anywhere along it. */
tbody tr:has(.row-link) { cursor: pointer; }
td.logged, td.numeric { color: var(--ink); white-space: nowrap; font-variant-numeric: tabular-nums; }

/* One status vocabulary for the whole console. Five pages had each invented a
   class for the same object — .flag, .battery-status, .deployment-status,
   .maintenance-type, .event-type — and every one of them carried its state in
   colour alone, which is the one thing DESIGN.md forbids a status to do. This
   is the single component: a tint, its matching on-tint text tone, and a
   leading shape, so the state survives a monochrome screen and an operator
   who does not see the tint. The word in the chip always states the state;
   the tint and the shape only second it. */
.flag {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  padding: .2em .55em;
  border: 1px solid currentColor;
  border-radius: var(--radius-control);
  font-family: var(--font-mono);
  font-size: 11.5px;
  line-height: 1.4;
  font-weight: 700;
  letter-spacing: .04em;
  white-space: nowrap;
}
.flag::before { font-size: 9px; line-height: 1; }
.flag.confirmed { background: var(--confirmed-soft); color: var(--confirmed-text); }
.flag.confirmed::before { content: "\25CF"; }
.flag.attention { background: var(--attention-soft); color: var(--attention-text); }
.flag.attention::before { content: "\25B2"; }
.flag.critical { background: var(--critical-soft); color: var(--critical-text); }
.flag.critical::before { content: "\25A0"; }
/* A neutral fact is the support site's flat pill; work underway takes the
   information blue and a play mark, so it does not read as merely scheduled. */
.flag.information { background: var(--panel-header); color: var(--muted); }
.flag.information::before { content: "\25C6"; }
.flag.information.is-active { background: var(--information-soft); color: var(--information); }
.flag.information.is-active::before { content: "\25B6"; }

/* Bar rows, as erektor.support draws them: a label, a thin rounded track
   whose fill is the value against the largest, and the figure in monospace. */
.chart { display: grid; padding: 14px 18px 16px; }
.chartrow {
  display: grid;
  grid-template-columns: minmax(130px, 1fr) minmax(0, 2.4fr) 6rem;
  align-items: center;
  column-gap: 22px;
  padding: 8px 0;
  border-top: 1px dashed var(--line);
}
.chartrow:first-child { border-top: 0; padding-top: 0; }
.chartrow:last-child { padding-bottom: 0; }
.chartrow__k { font-size: 14px; font-weight: 600; }
.chartrow__track { height: 7px; border-radius: 4px; background: var(--chart-track); overflow: hidden; }
.chartrow__fill { display: block; height: 100%; min-width: 2px; border-radius: 4px; background: var(--chart-bar); }
.chartrow__v { text-align: right; font-family: var(--font-mono); font-size: 12.5px; font-variant-numeric: tabular-nums; }
@media (max-width: 620px) {
  .chartrow { grid-template-columns: minmax(0, 1fr) auto; row-gap: 6px; }
  .chartrow__track { grid-column: 1 / -1; grid-row: 2; }
}
/* A level in a cell: the same track, coloured by how much room is left. */
.meter { display: inline-flex; align-items: center; gap: 9px; min-width: 130px; }
.meter__track { flex: 1 1 auto; height: 7px; min-width: 60px; border-radius: 4px; background: var(--chart-track); overflow: hidden; }
.meter__fill { display: block; height: 100%; min-width: 2px; border-radius: 4px; }
.meter__fill.confirmed { background: var(--confirmed); }
.meter__fill.attention { background: var(--attention); }
.meter__fill.critical { background: var(--critical); }
.meter b { font-family: var(--font-mono); font-size: 12.5px; font-weight: 700; font-variant-numeric: tabular-nums; }

/* Below phone width a wide table becomes a stack of cards, as on
   erektor.support: the first cell heads the card and every other cell is
   labelled from its column header (data-label, set by assets/ers-table.js). */
@media (max-width: 640px) {
  .line-panel .table-scroll table { min-width: 0; }
  .line-panel thead { display: none; }
  .line-panel table, .line-panel tbody, .line-panel tbody tr, .line-panel tbody td { display: block; width: 100%; }
  .line-panel tbody tr { padding: 12px 14px; border-top: 1px solid var(--line); }
  .line-panel tbody tr:first-child { border-top: 0; }
  .line-panel tbody td { display: flex; justify-content: space-between; align-items: center; gap: 12px; padding: 4px 0; border: 0; white-space: normal; }
  .line-panel tbody td[data-label]::before {
    content: attr(data-label);
    flex: none;
    color: var(--muted);
    font-family: var(--font-ui);
    font-size: 11px;
    font-weight: 700;
    letter-spacing: .1em;
    text-transform: uppercase;
  }
  .line-panel tbody td:first-child { padding-bottom: 6px; }
  .line-panel tbody td:first-child::before { content: none; }
  .line-panel tbody tr:hover td { background: none; }
}

/* Row-level controls. Both clear the 44px the console settled on for a floor
   workstation being operated with gloves on. */
.row-action {
  border: 1px solid var(--ink);
  background: var(--surface);
  color: var(--ink);
  border-radius: var(--radius-control);
  min-height: 44px;
  padding: 7px 13px;
  font-weight: 800;
  font-size: var(--type-meta);
  white-space: nowrap;
}
.row-action:hover { background: var(--paper); }
.row-action.is-urgent { background: var(--ink); color: var(--on-ink); }
.row-action.is-urgent:hover { background: var(--ink-pressed); }
.row-link {
  display: inline-flex;
  align-items: center;
  min-height: 44px;
  padding: 0 4px;
  margin-left: -4px;
  color: var(--ink);
  font-weight: 800;
  font-size: var(--type-meta);
  text-decoration: underline;
  text-underline-offset: 3px;
  white-space: nowrap;
}
.row-link:hover { color: var(--ink-strong); text-decoration-thickness: 2px; }

/* Leg detail sheet. Opened by a row's View control, this is the one asset
   record a page is allowed to present as a focused overlay rather than a
   ruled row — everything inside it still reads as paper: flat panel fill,
   an ink border, ruled dividers between sections, no card stacking. The
   scrim and offset shadow match the console's one other overlay, the
   check-in menu in assets/ers-line.css, so an operator meets one dialog
   language across the console rather than two. */
.leg-sheet-backdrop {
  position: fixed;
  inset: 0;
  z-index: 20;
  background: rgba(23, 23, 23, .5);
  display: grid;
  place-items: center;
  padding: 24px;
}
.leg-sheet-backdrop[hidden] { display: none; }
.leg-sheet {
  width: 100%;
  max-width: 560px;
  max-height: calc(100vh - 48px);
  overflow-y: auto;
  background: var(--panel);
  border: 1px solid var(--ink);
  box-shadow: 0 14px 34px rgba(23, 23, 23, .3);
}
.leg-sheet-head {
  position: sticky;
  top: 0;
  display: flex;
  align-items: flex-start;
  gap: 14px;
  padding: 16px 18px;
  background: var(--panel);
  border-bottom: 1px solid var(--ink);
}
.leg-sheet-eyebrow {
  color: var(--muted);
  text-transform: uppercase;
  letter-spacing: .1em;
  font-weight: 700;
  font-size: var(--type-label);
  line-height: 1.2;
  margin-bottom: 4px;
}
.leg-sheet-head h2 { font-size: 22px; letter-spacing: 0; line-height: 1.05; }
.leg-sheet-head .flag { margin-top: 4px; }
.leg-sheet-close {
  margin-left: auto;
  width: 32px;
  height: 32px;
  flex: none;
  display: grid;
  place-items: center;
  border: 1px solid var(--line);
  background: var(--paper);
  color: var(--ink);
  border-radius: var(--radius-control);
  font-size: 18px;
  line-height: 1;
}
.leg-sheet-close:hover { border-color: var(--ink); }
.leg-sheet-body { padding: 4px 18px 18px; }
.leg-sheet-section { padding: 16px 0; border-top: 1px solid var(--line); }
.leg-sheet-section:first-child { border-top: 0; }
.leg-sheet-section h3 {
  font-size: var(--type-meta);
  text-transform: uppercase;
  letter-spacing: .08em;
  font-weight: 600;
  margin-bottom: 12px;
}
.leg-sheet-facts {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: 12px 20px;
  margin: 0;
}
.leg-sheet-facts dt { margin-bottom: 3px; }
.leg-sheet-facts dd {
  margin: 0;
  font-weight: 800;
  font-variant-numeric: tabular-nums;
  overflow-wrap: anywhere;
}
.leg-sheet-turntable {
  aspect-ratio: 5 / 4;
  border: 1px solid var(--line);
  background: var(--ink-strong);
  overflow: hidden;
}
.leg-sheet-turntable[hidden] { display: none; }
.leg-sheet-turntable iframe { display: block; width: 100%; height: 100%; border: 0; }
/* A side with no captured turntable model reports that plainly, in the
   section its model would occupy, rather than reusing mirrored geometry
   that would misstate what was actually captured. */
.leg-sheet-drawing-empty {
  border: 1px dashed var(--line);
  background: var(--paper);
  padding: 20px 16px;
  color: var(--muted);
  text-align: center;
}
.leg-sheet-drawing-empty[hidden] { display: none; }
.leg-sheet-section h3 + .leg-sheet-caption { margin: -4px 0 14px; }
.leg-sheet-caption { margin-top: 8px; color: var(--muted); font-size: var(--type-meta); line-height: 1.4; }

@media (max-width: 650px) {
  .leg-sheet-backdrop { padding: 12px; }
  .leg-sheet { max-width: 480px; max-height: 90vh; }
  .leg-sheet-facts { grid-template-columns: 1fr; }
}

/* Where a page reports the result of an action it just took. A status line in
   the panel, next to the rows it changed — not an alert() the operator has to
   dismiss before they can see whether anything moved. */
.panel-status {
  padding: 11px 18px;
  border-top: 1px solid var(--line);
  color: var(--ink);
  font-weight: 700;
}
.panel-status:empty { display: none; }
.panel-empty { padding: 22px 18px; color: var(--ink); }


@media (max-width: 980px) {
  .masthead { gap: 16px; padding: 0 18px; }
  .masthead .brand-name { display: none; }
  .masthead h1 { padding-left: 16px; }
  .subnav { padding: 8px 18px; }
  .masthead .facility span, .masthead .facility label { display: none; }
  .masthead .facility-picker { width: 200px; }
  .summary { grid-template-columns: repeat(2, 1fr); }
  .summary > :nth-child(2n) { border-right: 0; }
  .summary > :not(:nth-last-child(-n+2)) { border-bottom: 1px solid var(--line); }
}

/* Narrowest phones: a two-word title needs the avatar's room. */
@media (max-width: 360px) {
  .masthead .account-avatar { display: none; }
}
/* On a phone the masthead is two rows that do not share columns: the
   wordmark, title and account, then the facility across whatever the
   Service Console link leaves. A grid would size the facility to the
   title's column; a flex row broken by ::after lets each row fill. */
@media (max-width: 650px) {
  .masthead {
    display: flex;
    flex-wrap: wrap;
    gap: 0 10px;
    min-height: 60px;
    padding: 8px 16px 10px;
  }
  .masthead::after { content: ""; order: 1; flex-basis: 100%; }
  .masthead .brand-mark { width: 22px; height: 22px; }
  .masthead h1 { flex: 1; padding: 0; border: 0; font-size: 18px; }
  .masthead .account { gap: 8px; padding-left: 0; border-left: 0; }
  .masthead .account-avatar { width: 32px; height: 32px; font-size: 11px; }
  .masthead .signout { padding: 0 12px; }
  .masthead .facility { order: 2; flex: 1; min-width: 0; margin-top: 8px; }
  .masthead .support-link { order: 2; margin-top: 8px; margin-right: -4px; }
  .masthead .facility-picker { width: 100%; }
  .masthead .facility-picker, .panel-search input { font-size: 16px; }
  .subnav { padding: 8px 16px; }
  nav a { min-height: 40px; padding: 0 12px; font-size: 13px; }
  .topbar, .content { padding-left: max(18px, env(safe-area-inset-left)); padding-right: max(18px, env(safe-area-inset-right)); }
  .topbar { align-items: flex-start; flex-wrap: wrap; gap: 14px; }
  .topbar > div:first-child { min-width: 0; }
  .actions, .topbar .actions { width: 100%; flex-wrap: wrap; gap: 8px; justify-content: flex-start; }
  .storage-warning { flex-basis: 100%; order: 3; }
  .actions .status-chip { display: none; }
  .account-user { display: none; }
  .signout { white-space: nowrap; }
  .status-chip, .action, .filter { white-space: nowrap; }
  .summary { grid-template-columns: 1fr 1fr; }
  .summary > * { padding: 13px; }
  .summary strong { font-size: 25px; }
}

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: .01ms !important;
    animation-iteration-count: 1 !important;
    scroll-behavior: auto !important;
    transition-duration: .01ms !important;
  }
}

/* Decorative full-bleed backdrops must stay behind the interface.
   Keep the backdrop in the page's visible stacking context so it remains
   visible through transparent areas without covering readable content. */
.scene {
  position: fixed;
  inset: 0;
  z-index: 0;
  overflow: hidden;
  pointer-events: none;
}
.shell {
  position: relative;
  z-index: 1;
}

@media (max-width: 650px) {
  .line-panel { margin-top: 20px; }
  .panel-head { align-items: flex-start; flex-direction: column; padding: 14px; gap: 10px; }
  .panel-title h2 { font-size: 18px; }
  .panel-head p { font-size: 13px; }
  .live { font-size: 10px; }
  th, td { padding-left: 14px; padding-right: 14px; }
  .panel-status, .panel-empty { padding-left: 14px; padding-right: 14px; }
}

/* A summary strip carries four cells; the 1.35fr lead column is that case.
   Battery packs reports two pools of three, so it states its count and gets an
   even three-up that wraps once, instead of spilling into a second row whose
   first cell is a third wider than its neighbours. Below 980px the shell's own
   two-up rule takes over, so this is scoped above it. */
@media (min-width: 981px) {
  .summary[data-cells="6"] { grid-template-columns: repeat(3, 1fr); }
  .summary[data-cells="6"] > :nth-child(3n) { border-right: 0; }
  .summary[data-cells="6"] > :nth-child(-n+3) { border-bottom: 1px solid var(--line); }
}

/* First-login tour (assets/ers-tour.js). A ring around the control being
   explained, and a ruled card beside it in the console's own plate style. */
.tour-ring {
  position: fixed;
  z-index: 10000;
  border: 2px solid var(--brand);
  border-radius: var(--radius-control);
  box-shadow: 0 0 0 9999px rgba(20, 20, 20, .42);
  pointer-events: none;
  transition: left var(--motion-fast) var(--ease-standard), top var(--motion-fast) var(--ease-standard),
    width var(--motion-fast) var(--ease-standard), height var(--motion-fast) var(--ease-standard);
}
.tour-card {
  position: fixed;
  z-index: 10001;
  width: min(380px, calc(100vw - 24px));
  background: var(--panel);
  color: var(--ink);
  border: 1px solid var(--ink);
  border-top: 3px solid var(--brand);
  box-shadow: 0 12px 32px rgba(0, 0, 0, .22);
  padding: 16px 18px 14px;
}
.tour-card.centred { left: 50% !important; top: 50% !important; transform: translate(-50%, -50%); }
.tour-card.docked { left: 12px !important; right: 12px; bottom: 12px; top: auto !important; width: auto; }
.tour-count {
  color: var(--muted);
  text-transform: uppercase;
  letter-spacing: .1em;
  font-size: var(--type-label);
  font-weight: 700;
}
.tour-title { font-size: 18px; letter-spacing: -.02em; line-height: 1.2; margin-top: 6px; }
.tour-body { margin-top: 8px; line-height: var(--leading-body); }
.tour-actions { display: flex; align-items: center; gap: 8px; margin-top: 14px; }
.tour-spacer { flex: 1; }
.tour-actions button {
  min-height: 36px;
  padding: 7px 12px;
  border-radius: var(--radius-control);
  font: inherit;
  font-weight: 800;
  white-space: nowrap;
}
.tour-next { background: var(--ink); color: var(--on-ink); border: 1px solid var(--ink); }
.tour-next:hover { background: var(--ink-pressed); }
.tour-back { background: var(--surface); color: var(--ink); border: 1px solid var(--line); }
.tour-skip { background: none; border: 0; color: var(--muted); padding-left: 0 !important; text-decoration: underline; text-underline-offset: 2px; }
.tour-actions button:focus-visible { outline: 3px solid var(--focus); outline-offset: 2px; }
@media (prefers-reduced-motion: reduce) { .tour-ring { transition: none; } }


