/* [libtools] Service Portal - design system
   Instrumentation for machines that measure themselves. Dense, quiet, exact.
   Colour is bound to meaning, never to hierarchy. Every ramp value below clears WCAG AA
   at the size it is actually used; see the contrast notes inline. */

:root {
  /* Semantic. Fill/accent use only -- #10b981 and #f59e0b do not clear AA as text at ANY size,
     so numerals are never tinted with them. The -ink variants are for text on the matching tint. */
  --ok: #10b981;       --ok-ink: #0b7c5d;       --ok-tint: #e7f8f2;
  --caution: #f59e0b;  --caution-ink: #96600a;  --caution-tint: #fdf1de;
  --stop: #ef4444;     --stop-ink: #b91c1c;     --stop-tint: #fdeaea;
  --info: #3b82f6;     --info-ink: #1d4ed8;     --info-tint: #e6effe;

  /* Modula orange, off modula.us (--primary-color: #FAA21B). Brand chrome only: the top rule and
     the selected tab. It is within a few degrees of --caution, so the moment it carries data it
     starts reading as a warning -- nothing that reflects state may use it. */
  --brand: #faa21b;

  /* Ink ramp, Modula's own neutrals rather than a blue-grey: #262829 near-black, #575a5d body,
     #faf7f7 page, #e6e6e6 rules. --label is lightened only as far as 5.0:1, so it still clears AA
     at the 10-11px it is used at. */
  --ink: #262829;
  --head: #3d4143;
  --body: #575a5d;
  --label: #6c7073;
  --muted: #979a9c;
  --line: #e6e6e6;
  --rule: #f1efee;
  --bg: #faf7f7;
  --surface: #fff;
  --sunken: #fcfbfa;

  /* DIN is the industrial standard face -- machinery, instrument panels, road signs. Bahnschrift
     ships on Windows, DIN Alternate on macOS. No web fonts, no CDN, no dependency. */
  --din: "Bahnschrift", "DIN Alternate", "Segoe UI Variable Display", "Segoe UI Semibold", system-ui, sans-serif;
  --sans: "Segoe UI Variable Text", "Segoe UI", -apple-system, BlinkMacSystemFont, "Helvetica Neue", Arial, sans-serif;
  --mono: "Cascadia Mono", ui-monospace, "SF Mono", Menlo, Consolas, monospace;

  --r-inline: 3px;   /* badges, buttons, inputs */
  --r-box: 6px;      /* cards, panels */
  --shadow: 0 1px 2px #2628290a;
  --shadow-pop: 0 12px 32px #2628291f;
}

*, *::before, *::after { box-sizing: border-box; }

body {
  margin: 0;
  background: var(--bg);
  color: var(--body);
  font: 13px/1.5 var(--sans);
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
}

h1, h2, h3 { margin: 0; color: var(--head); font-weight: 600; letter-spacing: -0.01em; }
h1 { font-size: 15px; } h2 { font-size: 13px; } h3 { font-size: 12px; }
a { color: var(--info-ink); text-decoration-thickness: 1px; text-underline-offset: 2px; }
p { margin: 0 0 8px; }

/* Visible keyboard focus everywhere, and only for keyboard. */
:focus-visible { outline: 2px solid var(--info); outline-offset: 2px; border-radius: 2px; }
:focus:not(:focus-visible) { outline: none; }

/* ---------- type utilities ---------- */
.mono, .num { font-family: var(--mono); font-variant-numeric: tabular-nums; }
.num { text-align: right; }
.din { font-family: var(--din); }
.eyebrow {
  font-family: var(--din);
  font-size: 10px; font-weight: 600;
  letter-spacing: 0.09em; text-transform: uppercase;
  color: var(--label);
}
.label { font-size: 11px; color: var(--label); font-weight: 500; }
.muted { color: var(--label); }
.faint { color: var(--muted); }
.sm { font-size: 12px; } .xs { font-size: 11px; } .xxs { font-size: 10px; }
.strong { color: var(--head); font-weight: 600; }

/* ---------- layout primitives ---------- */
.row { display: flex; align-items: center; gap: 8px; }
.wrap { flex-wrap: wrap; }
.grow { flex: 1; min-width: 0; }
.right { margin-left: auto; }
.hide { display: none !important; }
/* Offscreen but readable by a screen reader -- the aria-live region toast() announces through. */
.sr-only { position: absolute; width: 1px; height: 1px; margin: -1px; padding: 0; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0; }
.grid { display: grid; gap: 10px; }
.g2 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
.g3 { grid-template-columns: repeat(3, minmax(0, 1fr)); }
.g4 { grid-template-columns: repeat(4, minmax(0, 1fr)); }
.stack { display: grid; gap: 16px; }

/* ---------- shell ---------- */
header.top {
  background: var(--surface);
  border-top: 3px solid var(--brand);
  border-bottom: 1px solid var(--line);
  position: sticky; top: 0; z-index: 20;
}
header.top .bar {
  display: flex; align-items: center; gap: 20px;
  height: 50px; padding: 0 16px;
  max-width: 1560px; margin: 0 auto;
}
.brand {
  font-family: var(--din);
  font-weight: 700; font-size: 15px;
  color: var(--ink); letter-spacing: 0.01em;
  white-space: nowrap;
}
/* Modula (the equipment) stacked over the platform label, both left-aligned to the same edge just
   right of the [libtools] wordmark. The Modula line is the only place the brand orange appears
   besides the rule above the header. */
.brand .plat {
  display: inline-flex; flex-direction: column; align-items: flex-start;
  vertical-align: middle; margin-left: 11px; line-height: 1.25;
}
.brand .plat .oem {
  font-style: normal; font-size: 10px; font-weight: 700; letter-spacing: 0.11em; text-transform: uppercase;
  color: var(--head); border-bottom: 2px solid var(--brand); padding-bottom: 1px;
}
.brand .plat .sub { color: var(--label); font-weight: 400; font-size: 11px; letter-spacing: 0; margin-top: 2px; }

nav.tabs { display: flex; gap: 2px; align-items: stretch; height: 100%; }
nav.tabs button {
  background: none; border: 0; cursor: pointer;
  padding: 0 11px; height: 100%;
  font: 500 12px var(--sans); color: var(--label);
  border-bottom: 2px solid transparent;
  display: flex; align-items: center; gap: 6px;
  white-space: nowrap;
}
nav.tabs button:hover { color: var(--ink); background: var(--rule); }
nav.tabs button[aria-selected="true"] { color: var(--ink); border-bottom-color: var(--brand); font-weight: 600; }
nav.tabs .n {
  font-family: var(--mono); font-size: 10px; font-weight: 600;
  background: var(--rule); color: var(--label);
  border-radius: 8px; padding: 1px 5px; min-width: 16px; text-align: center;
}
nav.tabs button[aria-selected="true"] .n { background: var(--ink); color: #fff; }
nav.tabs .n.alert { background: var(--stop-tint); color: var(--stop-ink); }

main { padding: 16px; max-width: 1560px; margin: 0 auto; }

/* ---------- section ---------- */
.section { margin-bottom: 14px; }
.section > .sec-hd {
  display: flex; align-items: baseline; gap: 10px;
  margin-bottom: 6px; padding-bottom: 4px;
  border-bottom: 1px solid var(--line);
}

/* ---------- instrument bar (metrics) ----------
   One continuous segmented readout, not floating cards. Semantic colour rides a 2px accent rule,
   never the numeral, because the numeral colour would fail contrast. */
.instrument {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(170px, 1fr));
  background: var(--surface);
  border: 1px solid var(--line); border-radius: var(--r-box);
  box-shadow: var(--shadow);
  overflow: hidden;
}
.seg-metric {
  padding: 8px 12px 9px;
  /* border:0 first -- these are <button> when clickable, and the UA border would otherwise show */
  border: 0; border-left: 1px solid var(--rule);
  position: relative;
  text-align: left;
  background: none; font: inherit; color: inherit;
}
.seg-metric:first-child { border-left: 0; }
.seg-metric::before {
  content: ""; position: absolute; inset: 0 auto auto 0;
  height: 2px; width: 100%; background: var(--line);
}
.seg-metric.ok::before { background: var(--ok); }
.seg-metric.caution::before { background: var(--caution); }
.seg-metric.stop::before { background: var(--stop); }
.seg-metric.info::before { background: var(--info); }
.seg-metric .v {
  font-family: var(--din); font-size: 22px; font-weight: 700;
  color: var(--ink); line-height: 1.1; letter-spacing: -0.015em;
  font-variant-numeric: tabular-nums;
}
.seg-metric .k { font-size: 11px; color: var(--head); font-weight: 600; margin-top: 1px; }
.seg-metric .s { font-size: 10px; color: var(--label); margin-top: 1px; }
button.seg-metric { cursor: pointer; }
button.seg-metric:hover { background: var(--sunken); }

/* ---------- horizon (signature) ----------
   12 months of forecast service load across the fleet. Each visit is a marker; the hairline
   running into it is the parts lead time, so an order-by date reads as a physical runway. */
.horizon { background: var(--surface); border: 1px solid var(--line); border-radius: var(--r-box); box-shadow: var(--shadow); }
.horizon-scale {
  position: relative; height: 22px; margin-left: var(--lane-label-w, 168px);
  border-bottom: 1px solid var(--line);
}
.horizon-scale .tick {
  position: absolute; top: 0; bottom: 0;
  border-left: 1px solid var(--rule);
  padding-left: 4px;
  font-family: var(--din); font-size: 9px; font-weight: 600; letter-spacing: 0.06em;
  color: var(--muted); text-transform: uppercase;
  display: flex; align-items: center;
  white-space: nowrap;
}
.horizon-scale .tick.q { border-left-color: var(--line); color: var(--label); }

.horizon-lane { display: flex; align-items: stretch; border-top: 1px solid var(--rule); }
.horizon-lane:first-of-type { border-top: 0; }
.horizon-lane:hover { background: var(--sunken); }
.lane-label {
  width: var(--lane-label-w, 168px); flex: none;
  padding: 7px 10px; border-right: 1px solid var(--line);
  overflow: hidden;
}
.lane-label .n { font-size: 11px; color: var(--head); font-weight: 600; white-space: nowrap; text-overflow: ellipsis; overflow: hidden; }
.lane-label .s { font-family: var(--mono); font-size: 9px; color: var(--muted); }
.lane-track { position: relative; flex: 1; min-height: 34px; }

/* month gridlines behind the markers */
.lane-track .grid-line { position: absolute; top: 0; bottom: 0; border-left: 1px solid var(--rule); }

.visit {
  position: absolute; top: 50%; transform: translate(-50%, -50%);
  display: flex; align-items: center; gap: 3px;
  padding: 2px 5px; border-radius: var(--r-inline);
  font-family: var(--din); font-size: 10px; font-weight: 700; letter-spacing: 0.02em;
  white-space: nowrap; cursor: pointer;
  border: 1px solid transparent;
}
.visit.ok { background: var(--ok-tint); color: var(--ok-ink); border-color: #10b98155; }
.visit.caution { background: var(--caution-tint); color: var(--caution-ink); border-color: #f59e0b66; }
.visit.stop { background: var(--stop-tint); color: var(--stop-ink); border-color: #ef444466; }
.visit:hover { box-shadow: 0 0 0 3px #26282914; z-index: 3; }
.lead {
  position: absolute; top: 50%; height: 0;
  border-top: 1px dashed var(--caution);
  opacity: 0.75;
}
.lead::before {
  content: ""; position: absolute; left: 0; top: -3px;
  width: 1px; height: 7px; background: var(--caution);
}
.horizon-legend {
  display: flex; gap: 14px; flex-wrap: wrap;
  padding: 8px 12px; border-top: 1px solid var(--line);
  font-size: 10px; color: var(--label); background: var(--sunken);
}

/* ---------- card ---------- */
.card {
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--r-box);
  box-shadow: var(--shadow);
  margin-bottom: 12px;
}
.card > .hd {
  display: flex; align-items: center; gap: 10px;
  padding: 7px 11px; border-bottom: 1px solid var(--rule);
}
.card > .bd { padding: 10px; }
.card > .bd.flush { padding: 0; }
.card.unvalidated { border-color: #ef444466; }

/* ---------- table ---------- */
.tbl-wrap { overflow-x: auto; }
table { width: 100%; border-collapse: collapse; font-size: 12px; }
th {
  font-family: var(--din);
  font-size: 10px; font-weight: 600; letter-spacing: 0.07em; text-transform: uppercase;
  color: var(--label); text-align: left;
  padding: 7px 10px; white-space: nowrap;
  background: var(--sunken);
  border-bottom: 1px solid var(--line);
}
th.num, td.num { text-align: right; }
td { padding: 8px 10px; border-bottom: 1px solid var(--rule); vertical-align: middle; }
tbody tr:last-child td { border-bottom: 0; }
tbody tr.clickable { cursor: pointer; }
tbody tr.clickable:hover { background: var(--sunken); }
tbody tr.clickable:focus-visible { outline-offset: -2px; }
/* red rail: survey values are assumed, not confirmed */
tr.unvalidated > td:first-child { box-shadow: inset 2px 0 0 var(--stop); }

/* ---------- badge ---------- */
.badge {
  display: inline-block;
  font-family: var(--mono); font-size: 10px; font-weight: 700;
  padding: 2px 5px; border-radius: var(--r-inline);
  white-space: nowrap; line-height: 1.4;
}
.badge.ok { background: var(--ok-tint); color: var(--ok-ink); }
.badge.caution, .badge.warn { background: var(--caution-tint); color: var(--caution-ink); }
.badge.bad, .badge.stop { background: var(--stop-tint); color: var(--stop-ink); }
.badge.info { background: var(--info-tint); color: var(--info-ink); }
.badge.grey { background: var(--rule); color: var(--label); }
/* A badge that is also a link -- the status word doubles as the way to open what is behind it,
   rather than sitting next to a button repeating the same word. */
a.badge { text-decoration: none; }
a.badge:hover { text-decoration: underline; text-underline-offset: 2px; filter: brightness(0.97); }
.provisional {
  border: 1px solid #ef444455; background: var(--stop-tint);
  border-radius: var(--r-inline); padding: 1px 4px; color: var(--stop-ink);
}

/* level ladder chips */
.lv { display: inline-flex; gap: 2px; }
.lv b {
  font-family: var(--din); font-size: 10px; font-weight: 700;
  width: 16px; height: 16px; display: grid; place-items: center;
  border-radius: 2px; background: var(--rule); color: var(--muted);
}
.lv b.on { background: var(--ink); color: #fff; }

/* ---------- controls ---------- */
/* .btn, not button.btn -- an <a class="btn"> must style identically, and a type-scoped selector
   would leave it as bare link text. */
.btn {
  font: 500 12px var(--sans);
  padding: 5px 10px; border-radius: var(--r-inline);
  border: 1px solid var(--line); background: var(--surface); color: var(--head);
  cursor: pointer; white-space: nowrap;
  display: inline-block; text-decoration: none; line-height: 1.5;
}
.btn:hover { border-color: var(--muted); background: var(--sunken); color: var(--ink); }
.btn:active { background: var(--rule); }
.btn.pri { background: var(--ink); border-color: var(--ink); color: #fff; }
.btn.pri:hover { background: #3d4143; border-color: #3d4143; color: #fff; }
.btn.danger { color: var(--stop-ink); border-color: #ef444455; }
.btn:disabled { opacity: 0.45; cursor: not-allowed; }
.btn.sm { padding: 3px 7px; font-size: 11px; }

input, select, textarea {
  font: 12px var(--sans);
  padding: 5px 7px;
  border: 1px solid var(--line); border-radius: var(--r-inline);
  background: var(--surface); color: var(--ink); width: 100%;
}
textarea { resize: vertical; }
input:hover, select:hover, textarea:hover { border-color: var(--muted); }
input.num { font-family: var(--mono); text-align: right; }
label.f { display: block; margin-bottom: 8px; }
label.f > span {
  display: block;
  font-family: var(--din); font-size: 10px; font-weight: 600;
  letter-spacing: 0.07em; text-transform: uppercase;
  color: var(--label); margin-bottom: 3px;
}
/* assumed survey values: red until a human confirms them */
input.unvalidated, select.unvalidated { border-color: var(--stop); background: #ef44440a; }

/* A select whose current value IS the status carries the semantic tint itself. Pairing a coloured
   badge with a select that spells out the same word prints it twice; the control wins, because it
   is the thing you act on. Matches .badge metrics so the two read as the same object at a glance. */
select.status {
  width: auto;
  font-family: var(--mono); font-size: 10px; font-weight: 700;
  padding: 3px 20px 3px 6px;
}
select.status.ok { background: var(--ok-tint); color: var(--ok-ink); border-color: #10b98155; }
select.status.warn { background: var(--caution-tint); color: var(--caution-ink); border-color: #f59e0b66; }
select.status.bad { background: var(--stop-tint); color: var(--stop-ink); border-color: #ef444466; }
select.status.info { background: var(--info-tint); color: var(--info-ink); border-color: #3b82f655; }
select.status option { font-weight: 400; background: var(--surface); color: var(--ink); }

/* ---------- drawer ---------- */
.scrim { position: fixed; inset: 0; background: #26282940; z-index: 30; animation: fade .12s ease-out; }
.drawer {
  position: fixed; top: 0; right: 0; bottom: 0;
  width: min(780px, 96vw); background: var(--bg);
  border-left: 1px solid var(--line); z-index: 31;
  overflow: auto; box-shadow: var(--shadow-pop);
  animation: slide .16s cubic-bezier(.32, .72, 0, 1);
}
.drawer > .hd {
  position: sticky; top: 0; z-index: 2;
  background: var(--surface); border-bottom: 1px solid var(--line);
  padding: 11px 16px; display: flex; align-items: center; gap: 10px;
}
.drawer > .bd { padding: 16px; }
/* A handbook page is a landscape table up to ~500 columns wide. It still scrolls, but starting
   wider means the common pages need no scrolling at all. */
.drawer.wide { width: min(1180px, 98vw); }
@keyframes slide { from { transform: translateX(14px); opacity: 0 } }
@keyframes fade { from { opacity: 0 } }

/* ---------- notes, empty, toast ---------- */
.note {
  font-size: 11px; line-height: 1.5; color: var(--body);
  background: var(--surface); border: 1px solid var(--line);
  border-left: 2px solid var(--info);
  border-radius: var(--r-inline); padding: 8px 10px;
}
.note.warn { border-left-color: var(--caution); }
.note.bad { border-left-color: var(--stop); }
.note b { color: var(--head); }

.empty { padding: 18px 14px; text-align: center; color: var(--label); font-size: 12px; }

/* ---------- handbook source text ----------
   These pages are tables whose meaning is their column alignment, so the one thing this must never
   do is wrap. A wide row scrolls inside its own box; the page body still never scrolls sideways. */
.page-text {
  font-family: var(--mono); font-size: 10.5px; line-height: 1.6; color: var(--body);
  white-space: pre; overflow-x: auto; margin: 0; padding: 10px; tab-size: 4;
}
.snip { overflow-x: auto; font-family: var(--mono); font-size: 10.5px; line-height: 1.55; }
.snip .ln { white-space: pre; color: var(--muted); padding: 1px 6px; min-width: max-content; }
.snip .ln.on { color: var(--ink); box-shadow: inset 2px 0 0 var(--caution); background: #f59e0b0f; }
/* The promoted answer. Accent rides the top rule, never the text -- same rule as the metrics. */
.card.answer { border-color: #3b82f655; border-top: 2px solid var(--info); }
.card.answer > .hd { background: var(--sunken); }
.card.answer .snip .ln.on { box-shadow: none; background: none; color: var(--ink); white-space: pre-wrap; }
.empty .t { color: var(--head); font-weight: 600; margin-bottom: 2px; font-size: 12px; }

.bar-track { height: 4px; background: var(--rule); border-radius: 2px; overflow: hidden; }
.bar-fill { height: 100%; border-radius: 2px; }

.toast {
  position: fixed; bottom: 18px; left: 50%; transform: translateX(-50%);
  background: var(--ink); color: #fff;
  font-size: 12px; padding: 8px 14px; border-radius: 4px;
  z-index: 50; box-shadow: var(--shadow-pop);
  animation: rise .14s ease-out;
}
.toast.bad { background: var(--stop-ink); }
@keyframes rise { from { opacity: 0; transform: translate(-50%, 6px) } }

/* skeleton while the board loads, so the shell does not jump */
.skel { background: linear-gradient(90deg, var(--rule) 25%, var(--sunken) 37%, var(--rule) 63%); background-size: 400% 100%; animation: shim 1.2s infinite linear; border-radius: var(--r-inline); }
@keyframes shim { from { background-position: 100% 0 } to { background-position: 0 0 } }

/* ---------- responsive ---------- */
@media (max-width: 860px) {
  header.top .bar { height: auto; padding: 8px 12px; flex-wrap: wrap; gap: 8px; }
  nav.tabs { order: 3; width: 100%; overflow-x: auto; }
  nav.tabs button { height: 34px; }
  main { padding: 12px; }
  .g2, .g3, .g4 { grid-template-columns: 1fr; }
  .horizon-scale, .lane-label { --lane-label-w: 104px; }
  /* Twelve month labels will not fit; keep the gridlines, label only the quarters. */
  .horizon-scale .tick:not(.q) { font-size: 0; }
  /* Date and signed-in user are reference, not navigation. Reclaim the row for the controls. */
  .hide-narrow { display: none !important; }
}

/* ---------- print ----------
   The checklist has to survive being a PDF; the board and portal print as a clean data sheet --
   chrome and the wide colour chart (.no-print) drop out, the instrument bar and tables stay. The
   browser prints document.title (set per tab) and the date into the page margin. */
@media print {
  header.top, nav.tabs, .no-print, button, .toast, .scrim { display: none !important; }
  body { background: #fff; font-size: 10pt; }
  main { padding: 0; max-width: none; }
  .card, .instrument { break-inside: avoid; border-color: #bbb; box-shadow: none; }
  .section { margin-bottom: 10px; }
  .drawer { position: static; width: auto; box-shadow: none; border: 0; animation: none; }
  a { text-decoration: none; color: inherit; }
}

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

/* ---------- handbook split ----------
   Search and results on the left; the printed manual itself on the right, snapped to the cited
   page. The pane rides sticky under the app header. On narrow screens it drops out entirely and
   snapping opens the PDF in its own tab instead -- half of a phone screen shows neither well. */
.hb-split { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 46%); gap: 14px; align-items: start; }
.hb-pane { position: sticky; top: 60px; }
.hb-pdf {
  width: 100%; height: calc(100vh - 148px); min-height: 420px;
  border: 1px solid var(--line); border-radius: 6px; background: #525659;
}
@media (max-width: 999px) { .hb-split { grid-template-columns: 1fr; } .hb-pane { display: none; } }
