/* ==========================================================================
   FingerWorks QA — shared component vocabulary. Ticket 10388.
   ==========================================================================

   InvenTrack's shell and component shapes, this app's palette. The two are meant to read
   as one family: the same 240px sidebar with icon nav and uppercase section headings, the
   same 56px topbar, the same single scrolling content pane, the same Plus Jakarta Sans.
   What differs is the hue — violet on a warm graphite, against InvenTrack's periwinkle on
   cool navy — so nobody confuses which app they are in.

   TWO RULES GOVERN THIS FILE.

   1. EVERY CLASS NAME IN IT IS LOAD-BEARING AND NONE MAY BE RENAMED. Eight ticket branches
      under 10330 were written against this vocabulary while this restyle did not exist.
      Not one of them may need editing. InvenTrack's own names (stat-card, badge, form-group,
      grid-3, btn-ghost, nav-item …) are added as ALIASES at the end, so a page written in
      either app's idiom renders correctly.

   2. RESULT STATE COLOURS ARE SEMANTIC, NOT DECORATION, and colour is never the only
      channel. The spreadsheet this app replaces encodes pass and fail as red and green cell
      fill with no text, which is why nothing in it can be sorted, counted or exported. Here
      every state also carries its own word and its own shape, so a greyscale screenshot, a
      colour-blind reader and a CSV all say the same thing. The brand hue is deliberately
      chosen away from all five states — green is pass, red is fail, amber is blocked, blue
      is exercised — so the accent can never be misread as a result.
      A COROLLARY THAT IS EASY TO MISS: a colour borrowed from that set carries its meaning
      with it. The InvenTrack badge aliases near the end of this file are therefore kept off
      the --st-* palette entirely, because "ACTIVE" in pass green sitting beside a PASS chip
      is the same misreading arriving by the back door.

   3. EVERY TEXT PAIR CLEARS 4.5:1 IN BOTH THEMES, and both themes are checked together. A
      dark theme wants its brand and state colours light so they read against the surface,
      which is precisely what makes white text on those same colours unreadable — so on that
      side the ink that sits on a saturated fill is dark (--on-bright). Nothing here is large
      text: the chips are 10.5px and the buttons 13px, so 4.5 is the bar for all of it and
      3:1 never applies. The tokens carry the compliance, so a new component that uses them
      inherits it; a hard-coded #fff on a tint does not, which is how the three that failed
      got in. Round 1 of #10388 re-solved the whole palette against this — the review found
      three pairs, an audit of every pair in both themes found seventeen.

   Themes: light in :root, dark under [data-theme="dark"]. templates/header.php always
   stamps the attribute, and :root also carries a complete set so an unstamped document
   still renders correctly rather than borrowing half a palette.
   -------------------------------------------------------------------------- */

:root {
  color-scheme: light;

  /* Brand */
  --primary:      #7c3aed;
  --primary-ink:  #ffffff;
  --primary-soft: #f1ebfe;
  --primary-dim:  #a78bfa;
  --accent:       #994de1;
  /* Ink for text sitting ON a saturated fill (accent, danger). Light theme fills are dark
     enough to carry white; the dark theme's are not — see the note by --on-bright below. */
  --on-bright:    #ffffff;
  --glow:         rgba(124, 58, 237, .07);

  /* Surfaces and lines */
  --bg:         #f5f3fa;
  --surface:    #ffffff;
  --surface-2:  #faf9fd;
  --surface-alt:#faf9fd;
  --line:       #e5e1ef;
  --line-soft:  #efecf6;
  --border:     #e5e1ef;
  --border-light:#d3cde3;

  /* Text */
  --ink:        #1a1626;
  --ink-2:      #5b5571;
  --ink-3:      #746f84;
  --text:       #1a1626;
  --text-muted: #5b5571;
  --text-dim:   #746f84;

  /* Result states. Fill for chips, line for bars and borders. */
  --st-untested:      #656b78;
  --st-untested-soft: #eef0f4;
  --st-pass:          #047b56;
  --st-pass-soft:     #e3f5ee;
  --st-fail:          #ce2b1e;
  --st-fail-soft:     #fdeceb;
  --st-blocked:       #af5109;
  --st-blocked-soft:  #fdf1e0;
  --st-na:            #5e6d83;
  --st-na-soft:       #eef1f5;
  --st-exercised:     #0369a1;
  --st-exercised-soft:#e5f2fb;

  /* Semantic aliases InvenTrack pages use */
  --danger:  var(--st-fail);
  --warning: var(--st-blocked);

  --radius:    10px;
  --radius-s:  8px;
  --shadow:    0 1px 2px rgba(20, 12, 40, .05), 0 10px 30px rgba(20, 12, 40, .07);
  --transition: .18s cubic-bezier(.4, 0, .2, 1);

  --side-w: 240px;
  --gutter: 24px;

  --font: "Plus Jakarta Sans", system-ui, -apple-system, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
  --mono: "JetBrains Mono", ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
}

[data-theme="dark"] {
  color-scheme: dark;

  --primary:      #9367f7;
  --primary-ink:  #1b1226;
  --primary-soft: #1c1530;
  --primary-dim:  #6d45c4;
  --accent:       #c084fc;
  /* A dark theme wants its brand and state colours LIGHT so they read against the surface,
     which is exactly what makes white text on them unreadable: #fff on the old #8b5cf6 was
     4.2:1, on #c084fc 2.6:1 and on #fb7185 2.7:1. So on this side of the palette the ink that
     sits on a saturated fill is dark, not white. --primary-ink moves with it (#10388 round 1). */
  --on-bright:    #1b1226;
  --glow:         rgba(147, 103, 247, .10);

  --bg:         #12101a;
  --surface:    #1a1726;
  --surface-2:  #221e31;
  --surface-alt:#221e31;
  --line:       #2c2740;
  --line-soft:  #231f33;
  --border:     #2c2740;
  --border-light:#3a3352;

  --ink:        #ece9f5;
  --ink-2:      #a49ec0;
  --ink-3:      #8a86a5;
  --text:       #ece9f5;
  --text-muted: #a49ec0;
  --text-dim:   #8a86a5;

  --st-untested:      #868c9e;
  --st-untested-soft: #20242e;
  --st-pass:          #34d399;
  --st-pass-soft:     #11302a;
  --st-fail:          #fb7185;
  --st-fail-soft:     #341c26;
  --st-blocked:       #fbbf24;
  --st-blocked-soft:  #332715;
  --st-na:            #94a3b8;
  --st-na-soft:       #222732;
  --st-exercised:     #38bdf8;
  --st-exercised-soft:#12283a;

  --shadow: 0 1px 2px rgba(0, 0, 0, .4), 0 10px 30px rgba(0, 0, 0, .3);
}

/* --------------------------------------------------------------------------
   Base
   -------------------------------------------------------------------------- */
* { box-sizing: border-box; }

html, body { height: 100%; }

body {
  margin: 0;
  background: var(--bg);
  color: var(--ink);
  font: 14px/1.55 var(--font);
  -webkit-font-smoothing: antialiased;
}
/* One scroller: the content pane. The sidebar and topbar stay put, as in InvenTrack.
   `display: block` is load-bearing, not a default being restated. header.php puts the class
   `app` on BOTH the body and the shell wrapper inside it, so the `.app` rule below matches
   twice; without this the body became a row flex container and the wrapper, a flex item that
   does not grow, sized to its content. Any short page — an empty dashboard, the 404, a small
   settings form — then drew the whole shell narrower than the window with bare --bg beside
   it. `body.app` outranks `.app` on specificity, so the wrapper keeps the flex row and the
   body does not. Fixed this way rather than by renaming the wrapper because every class name
   in this file is a contract eight branches were written against (#10388 review round 1). */
body.app { display: block; overflow: hidden; }

a { color: var(--primary); text-decoration: none; }
a:hover { text-decoration: underline; }

h1, h2, h3 { margin: 0; line-height: 1.2; font-weight: 700; letter-spacing: -.01em; text-wrap: balance; }
h1 { font-size: 1.45rem; font-weight: 800; }
h2 { font-size: 1.08rem; }
h3 { font-size: .98rem; }

p { margin: .6rem 0; max-width: 72ch; }

code, .mono, .num { font-family: var(--mono); font-variant-numeric: tabular-nums; }
code {
  font-size: .86em;
  background: var(--surface-2);
  border: 1px solid var(--line-soft);
  border-radius: 5px;
  padding: .08em .36em;
}

:focus-visible { outline: 2px solid var(--primary); outline-offset: 2px; border-radius: 4px; }

.muted  { color: var(--ink-2); }
.dim    { color: var(--ink-3); }
.small  { font-size: .85rem; }
.right  { text-align: right; }
.center { text-align: center; }
.nowrap { white-space: nowrap; }
.tnum   { font-variant-numeric: tabular-nums; }
.hidden { display: none !important; }

/* --------------------------------------------------------------------------
   Shell — sidebar, topbar, content. InvenTrack's arrangement.
   -------------------------------------------------------------------------- */
.app { display: flex; height: 100vh; height: 100dvh; }

.sidebar, .side {
  width: var(--side-w);
  min-width: var(--side-w);
  background: var(--surface);
  border-right: 1px solid var(--line);
  display: flex;
  flex-direction: column;
}

.sidebar-header { display: flex; align-items: center; gap: 11px; padding: 18px 16px 12px; }
.sidebar-logo {
  width: 34px; height: 34px;
  border-radius: 10px;
  background: linear-gradient(135deg, var(--primary), var(--accent));
  display: grid; place-items: center;
  /* --on-bright, not #fff: this sits on the accent end of the gradient too, which in dark
     mode is #c084fc and carried white at 2.6:1. Clears 4.5 against BOTH stops in both
     themes — the gradient is why it has to be checked twice. */
  color: var(--on-bright); font-size: 12px; font-weight: 800; letter-spacing: .02em;
  flex: none;
}
.sidebar-title, .side .brand {
  font-weight: 800; font-size: 15px; letter-spacing: -.02em; color: var(--ink);
  min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.sidebar-title:hover, .side .brand:hover { text-decoration: none; }
.sidebar-title span, .side .brand span { color: var(--primary); }

.sidebar-cta { padding: 0 12px 6px; }
.btn-new { justify-content: flex-start; gap: 9px; }

.nav { flex: 1; padding: 4px 10px 10px; overflow-y: auto; }

.nav-item, .side nav a {
  display: flex; align-items: center; gap: 11px;
  padding: 9px 11px; margin: 1px 0;
  border-radius: var(--radius-s);
  color: var(--ink-2);
  font-size: 13.5px; font-weight: 500;
  position: relative;
  transition: background var(--transition), color var(--transition);
}
.nav-item:hover, .side nav a:hover { background: var(--surface-2); color: var(--ink); text-decoration: none; }
.nav-item.active, .nav-item.on, .side nav a.on {
  background: var(--glow); color: var(--primary); font-weight: 600;
}
.nav-item.active::before, .nav-item.on::before {
  content: ""; position: absolute; left: 0; top: 50%;
  transform: translateY(-50%);
  width: 3px; height: 20px;
  background: var(--primary);
  border-radius: 0 3px 3px 0;
}
.nav-icon { width: 20px; height: 18px; display: grid; place-items: center; flex: none; }
.nav-label { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

.nav-section, .nav-group {
  padding: 15px 11px 5px;
  font-size: 10px; font-weight: 700;
  text-transform: uppercase; letter-spacing: .12em;
  color: var(--ink-3);
}

.nav-badge {
  margin-left: auto;
  background: var(--danger); color: var(--on-bright);
  font-size: 10px; font-weight: 700;
  min-width: 18px; height: 18px; border-radius: 9px;
  display: grid; place-items: center; padding: 0 5px; line-height: 1;
}

.sidebar-footer, .side-foot { padding: 12px; border-top: 1px solid var(--line); }
.who { display: flex; align-items: center; gap: 9px; padding: 5px 6px 9px; color: var(--ink); }
.who:hover { text-decoration: none; }
.avatar {
  display: grid; place-items: center;
  width: 30px; height: 30px; border-radius: 9px;
  background: var(--primary-soft); color: var(--primary);
  border: 1px solid var(--line);
  font-size: 12px; font-weight: 700; flex: none;
}
.who-text { display: flex; flex-direction: column; line-height: 1.25; min-width: 0; }
.who-text .user-name, .who-text > span:first-child {
  font-weight: 700; font-size: 13px;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.who-text em, .who-text .user-role {
  font-style: normal; font-size: 11px; color: var(--ink-3);
  text-transform: uppercase; letter-spacing: .07em;
}

.main { flex: 1; display: flex; flex-direction: column; overflow: hidden; min-width: 0; }

.topbar {
  height: 56px; min-height: 56px;
  display: flex; align-items: center; gap: 14px;
  padding: 0 var(--gutter);
  background: var(--surface);
  border-bottom: 1px solid var(--line);
}
.topbar-title { font-weight: 700; color: var(--ink-2); }

.search-box { position: relative; flex: 1; max-width: 400px; }
.search-box .search-icon {
  position: absolute; left: 11px; top: 50%; transform: translateY(-50%);
  color: var(--ink-3); display: grid; place-items: center; pointer-events: none;
}
.search-box input { padding-left: 34px; }

.theme-switch {
  margin-left: auto;
  display: flex; gap: 2px; padding: 3px;
  background: var(--surface-2);
  border: 1px solid var(--line);
  border-radius: 999px;
}
.theme-opt {
  border: 0; background: transparent; color: var(--ink-3);
  font: inherit; font-size: 11.5px; font-weight: 600;
  padding: 4px 11px; border-radius: 999px; cursor: pointer;
}
.theme-opt:hover { color: var(--ink); }
.theme-opt.on { background: var(--surface); color: var(--primary); box-shadow: var(--shadow); }

.content, .wrap { flex: 1; overflow: auto; padding: 22px var(--gutter) 34px; min-width: 0; }
.wrap-narrow { max-width: 420px; margin: 0 auto; padding-top: 9vh; overflow: visible; }
body.anon { display: block; overflow: auto; }

.foot {
  display: flex; justify-content: space-between; gap: 12px;
  margin-top: 30px; padding-top: 14px;
  border-top: 1px solid var(--line-soft);
  color: var(--ink-3); font-size: .78rem;
}
.foot-anon { max-width: 420px; margin: 20px auto; padding: 0 20px; }

/* --------------------------------------------------------------------------
   Page headers
   -------------------------------------------------------------------------- */
.page-head {
  display: flex; flex-wrap: wrap; align-items: flex-end; justify-content: space-between;
  gap: 12px; margin-bottom: 18px;
}
.page-head .sub { color: var(--ink-2); font-size: .9rem; margin-top: 3px; }
.page-head .actions { display: flex; flex-wrap: wrap; gap: 8px; }

.section-head {
  display: flex; align-items: baseline; justify-content: space-between;
  gap: 12px; margin: 24px 0 10px;
}
.section-head h2 {
  font-size: 11px; text-transform: uppercase; letter-spacing: .1em;
  color: var(--ink-3); font-weight: 700;
}

.eyebrow {
  font-size: 10px; text-transform: uppercase; letter-spacing: .12em;
  color: var(--ink-3); font-weight: 700;
}

/* --------------------------------------------------------------------------
   Cards, tiles, panels
   -------------------------------------------------------------------------- */
.card {
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: 18px 20px;
  transition: border-color var(--transition);
}
.card:hover { border-color: var(--border-light); }
.card + .card { margin-top: 12px; }
.card-tight { padding: 0; overflow: hidden; }
.card-tight:hover { border-color: var(--line); }

.tiles, .grid-3 { display: grid; grid-template-columns: repeat(auto-fit, minmax(190px, 1fr)); gap: 12px; }

.tile, .stat-card {
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: 14px 16px;
  min-width: 0;
  transition: border-color var(--transition), transform var(--transition);
}
a.tile:hover, a.stat-card:hover { border-color: var(--primary-dim); text-decoration: none; }
.tile .k, .stat-card .label {
  font-size: 10px; text-transform: uppercase; letter-spacing: .11em;
  color: var(--ink-3); font-weight: 700;
}
.tile .v, .stat-card .value {
  font-size: 1.5rem; font-weight: 800; letter-spacing: -.02em;
  margin-top: 4px; line-height: 1.15; overflow-wrap: anywhere;
}
.tile .s { font-size: .82rem; color: var(--ink-2); margin-top: 2px; }
.tile .v.good { color: var(--st-pass); }
.tile .v.bad  { color: var(--st-fail); }
.tile .v.warn { color: var(--st-blocked); }
.tile .v.none { color: var(--ink-3); }
/* InvenTrack centres its stat cards and colours the number with the brand. */
.stat-card { text-align: center; }
.stat-card .value { color: var(--primary); font-size: 1.7rem; }
.stat-card .label { margin-top: 4px; }

.grid-2 { display: grid; grid-template-columns: repeat(auto-fit, minmax(320px, 1fr)); gap: 12px; }

/* --------------------------------------------------------------------------
   Tables
   -------------------------------------------------------------------------- */
.tablewrap { overflow-x: auto; }

table.data { width: 100%; border-collapse: collapse; font-size: .9rem; }
table.data th,
table.data td {
  text-align: left; padding: 10px 12px;
  border-bottom: 1px solid var(--line-soft);
  vertical-align: top;
}
table.data thead th {
  font-size: 10px; text-transform: uppercase; letter-spacing: .1em;
  color: var(--ink-3); font-weight: 700;
  border-bottom: 1px solid var(--line);
  white-space: nowrap;
  position: sticky; top: 0; z-index: 1;
  background: var(--surface);
}
table.data tbody tr:hover { background: var(--glow); }
table.data tbody tr:last-child td { border-bottom: 0; }
table.data td.num, table.data th.num { text-align: right; font-variant-numeric: tabular-nums; white-space: nowrap; }
table.data td.shrink { width: 1%; white-space: nowrap; }
tr.clickable { cursor: pointer; }

/* --------------------------------------------------------------------------
   State chips — word + colour + shape. Never colour alone.
   -------------------------------------------------------------------------- */
.st {
  display: inline-flex; align-items: center; gap: 5px;
  padding: 2px 9px 2px 7px;
  border-radius: 999px;
  font-size: 10.5px; font-weight: 700;
  letter-spacing: .05em; text-transform: uppercase;
  white-space: nowrap;
  border: 1px solid transparent;
}
.st::before { content: ""; width: 7px; height: 7px; border-radius: 50%; background: currentColor; flex: none; }
.st-untested  { color: var(--st-untested);  background: var(--st-untested-soft);  border-color: var(--line); }
.st-untested::before { background: transparent; border: 1.5px solid currentColor; }
.st-pass      { color: var(--st-pass);      background: var(--st-pass-soft); }
.st-fail      { color: var(--st-fail);      background: var(--st-fail-soft); }
.st-blocked   { color: var(--st-blocked);   background: var(--st-blocked-soft); }
.st-blocked::before { border-radius: 1px; transform: rotate(45deg); }
.st-na        { color: var(--st-na);        background: var(--st-na-soft); }
.st-na::before { width: 9px; height: 1.5px; border-radius: 0; }
.st-exercised { color: var(--st-exercised); background: var(--st-exercised-soft); }
.st-exercised::before { border-radius: 1px; }

/* Neutral label chip, for things that are not result states. */
.chip, .tag, .badge {
  display: inline-block;
  padding: 2px 9px;
  border-radius: 999px;
  border: 1px solid var(--line);
  background: var(--surface-2);
  color: var(--ink-2);
  font-size: 10.5px; font-weight: 700; letter-spacing: .05em;
  white-space: nowrap;
}
.chip-accent { color: var(--primary); border-color: var(--primary-dim); background: var(--primary-soft); }
.chip-warn   { color: var(--st-blocked); border-color: transparent; background: var(--st-blocked-soft); }
.chip-bad    { color: var(--st-fail);    border-color: transparent; background: var(--st-fail-soft); }

/* InvenTrack badge names. NOT ONE OF THEM MAY USE A RESULT COLOUR — rule 2 at the top of
   this file. They describe a lifecycle (a machine is active, a build is deployed), and the
   pages that will carry them are the very pages that also carry result chips. Mapped onto
   --st-* they read as results: "ACTIVE" in pass green beside a PASS chip, "RETIRED" in fail
   red beside a FAIL one, "DEPLOYED" in the blue that means exercised. That is the misreading
   this app exists to make impossible, and a restyle is exactly how it creeps back in
   (#10388 review round 1).

   So the whole set lives in the non-result palette — the brand violet and the three neutral
   inks — and distinguishes itself by word and by emphasis instead of by hue. A page that
   genuinely wants to shout still has .chip-warn and .chip-bad, which are deliberate rather
   than inherited from a port. Verified no branch under 10330 uses a badge-* class, so this
   remap costs none of them an edit. */
.badge-accent                   { color: var(--primary); background: var(--primary-soft); border-color: var(--primary-dim); }
.badge-active, .badge-available,
.badge-deployed, .badge-testing { color: var(--ink);     background: var(--surface-2);    border-color: var(--border-light); }
.badge-open, .badge-maintenance { color: var(--ink-2);   background: var(--surface-2);    border-color: var(--border-light); }
.badge-inactive, .badge-retired { color: var(--ink-3);   background: var(--surface-2);    border-color: var(--line); }

/* --------------------------------------------------------------------------
   Progress bar — the composition of a run or a build
   -------------------------------------------------------------------------- */
.bar {
  display: flex; height: 8px;
  border-radius: 999px; overflow: hidden;
  background: var(--st-untested-soft);
  border: 1px solid var(--line-soft);
}
.bar span { display: block; height: 100%; }
.bar .b-pass      { background: var(--st-pass); }
.bar .b-fail      { background: var(--st-fail); }
.bar .b-blocked   { background: var(--st-blocked); }
.bar .b-na        { background: var(--st-na); }
.bar .b-exercised { background: var(--st-exercised); }
.bar-lg { height: 12px; }

.legend { display: flex; flex-wrap: wrap; gap: 4px 14px; margin-top: 8px; font-size: .78rem; color: var(--ink-2); }
.legend i { display: inline-block; width: 8px; height: 8px; border-radius: 2px; margin-right: 5px; }

/* --------------------------------------------------------------------------
   Forms
   -------------------------------------------------------------------------- */
.field, .form-group { display: flex; flex-direction: column; gap: 5px; margin-bottom: 13px; min-width: 0; }
.field > label, .form-label {
  font-size: 11px; text-transform: uppercase; letter-spacing: .08em;
  color: var(--ink-2); font-weight: 700;
}
.field .hint { font-size: .8rem; color: var(--ink-3); }
.field .err  { font-size: .82rem; color: var(--st-fail); font-weight: 600; }

.fieldrow, .form-row { display: grid; grid-template-columns: repeat(auto-fit, minmax(180px, 1fr)); gap: 0 14px; }

.form-actions {
  display: flex; gap: 10px; justify-content: flex-end;
  padding-top: 14px; margin-top: 14px; border-top: 1px solid var(--line);
}

input[type=text], input[type=email], input[type=password], input[type=number],
input[type=search], input[type=url], input[type=date], input[type=datetime-local],
input[type=time], select, textarea {
  width: 100%;
  background: var(--surface-2);
  color: var(--ink);
  border: 1px solid var(--line);
  border-radius: var(--radius-s);
  padding: 9px 11px;
  font: inherit;
  min-width: 0;
  transition: border-color var(--transition), box-shadow var(--transition);
}
input:focus, select:focus, textarea:focus {
  outline: none;
  border-color: var(--primary);
  box-shadow: 0 0 0 3px var(--glow);
}
input::placeholder, textarea::placeholder { color: var(--ink-3); }
input[aria-invalid="true"], .input-bad { border-color: var(--st-fail); }
textarea { resize: vertical; min-height: 78px; }
select {
  appearance: none;
  background-image: linear-gradient(45deg, transparent 50%, var(--ink-3) 50%),
                    linear-gradient(135deg, var(--ink-3) 50%, transparent 50%);
  background-position: right 14px center, right 9px center;
  background-size: 5px 5px, 5px 5px;
  background-repeat: no-repeat;
  padding-right: 28px;
}

.check { display: flex; align-items: center; gap: 8px; font-size: .9rem; }
.check input { width: auto; }

fieldset { border: 1px solid var(--line); border-radius: var(--radius); padding: 14px 16px; margin: 0 0 14px; }
legend { padding: 0 6px; font-size: 11px; text-transform: uppercase; letter-spacing: .08em; color: var(--ink-2); font-weight: 700; }

/* --------------------------------------------------------------------------
   Buttons
   -------------------------------------------------------------------------- */
.btn {
  display: inline-flex; align-items: center; justify-content: center; gap: 7px;
  padding: 8px 16px;
  border-radius: var(--radius-s);
  border: 1px solid var(--line);
  background: var(--surface-2);
  color: var(--ink);
  font: inherit; font-size: 13px; font-weight: 600;
  cursor: pointer; white-space: nowrap;
  transition: background var(--transition), border-color var(--transition), transform var(--transition);
}
.btn:hover { background: var(--surface); border-color: var(--border-light); text-decoration: none; transform: translateY(-1px); }
.btn:active { transform: translateY(0); }
.btn:disabled, .btn[aria-disabled="true"] { opacity: .5; cursor: not-allowed; transform: none; }

.btn-primary { background: var(--primary); border-color: var(--primary); color: var(--primary-ink); }
.btn-primary:hover { background: var(--primary); border-color: var(--primary); box-shadow: 0 4px 16px var(--glow); }
.btn-accent  { background: var(--accent); border-color: var(--accent); color: var(--on-bright); }
.btn-danger  { background: var(--st-fail-soft); border-color: transparent; color: var(--st-fail); }
.btn-danger:hover { background: var(--st-fail); color: var(--on-bright); }
.btn-quiet, .btn-ghost { background: transparent; color: var(--ink-2); }
.btn-quiet:hover, .btn-ghost:hover { background: var(--surface-2); color: var(--ink); }
.btn-sm { padding: 5px 11px; font-size: 12px; }
.btn-lg { padding: 11px 22px; font-size: 14.5px; }
.btn-block { width: 100%; }

.btnbar { display: flex; flex-wrap: wrap; gap: 8px; align-items: center; }
form.inline { display: inline; }

/* --------------------------------------------------------------------------
   Filters
   -------------------------------------------------------------------------- */
.filters {
  display: flex; flex-wrap: wrap; align-items: flex-end; gap: 0 12px;
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: 14px 16px 2px;
  margin-bottom: 14px;
}
.filters .field, .filters .form-group { min-width: 150px; flex: 1 1 150px; }
.filters .btnbar { margin-bottom: 13px; }

/* --------------------------------------------------------------------------
   Flash, empty states, notes
   -------------------------------------------------------------------------- */
.flash {
  border-radius: var(--radius);
  padding: 11px 15px;
  margin-bottom: 16px;
  border: 1px solid var(--line);
  background: var(--surface);
  font-weight: 500;
}
.flash-success { border-color: transparent; background: var(--st-pass-soft);    color: var(--st-pass); }
.flash-error   { border-color: transparent; background: var(--st-fail-soft);    color: var(--st-fail); }
.flash-warning { border-color: transparent; background: var(--st-blocked-soft); color: var(--st-blocked); }

.empty {
  text-align: center;
  padding: 40px 22px;
  color: var(--ink-2);
  border: 1px dashed var(--line);
  border-radius: var(--radius);
  background: var(--surface);
}
.empty h3 { color: var(--ink); margin-bottom: 6px; }
.empty p { margin-left: auto; margin-right: auto; }

.note-box {
  border-left: 3px solid var(--line);
  padding: 2px 0 2px 10px;
  color: var(--ink-2);
  font-size: .86rem;
  white-space: pre-wrap;
  overflow-wrap: anywhere;
}
.note-box.is-fail { border-left-color: var(--st-fail); }

/* --------------------------------------------------------------------------
   Keyboard hints — the run page is keyboard-driven and says so
   -------------------------------------------------------------------------- */
kbd {
  display: inline-block; min-width: 1.5em; text-align: center;
  padding: 1px 5px;
  border: 1px solid var(--line); border-bottom-width: 2px;
  border-radius: 5px;
  background: var(--surface-2);
  font-family: var(--mono); font-size: .76rem;
  color: var(--ink);
}

/* --------------------------------------------------------------------------
   Drawer
   -------------------------------------------------------------------------- */
.drawer {
  position: fixed; top: 0; right: 0; bottom: 0;
  width: min(560px, 100%);
  background: var(--surface);
  border-left: 1px solid var(--line);
  box-shadow: var(--shadow);
  padding: 20px 22px;
  overflow-y: auto;
  z-index: 40;
}
.drawer-head { display: flex; justify-content: space-between; align-items: flex-start; gap: 12px; margin-bottom: 14px; }
.scrim { position: fixed; inset: 0; background: rgba(12, 8, 22, .55); z-index: 39; }

/* --------------------------------------------------------------------------
   Sparkline and chart helpers
   -------------------------------------------------------------------------- */
.spark { display: flex; align-items: flex-end; gap: 2px; height: 26px; }
.spark i { display: block; flex: 1 1 3px; min-width: 2px; background: var(--primary-dim); border-radius: 2px 2px 0 0; }
.spark i.hot { background: var(--st-fail); }

.chart { width: 100%; height: auto; display: block; max-width: 100%; }
.chart text { fill: var(--ink-2); font-family: var(--font); font-size: 11px; }
.chart .grid line { stroke: var(--line-soft); stroke-width: 1; }
.chart .axis line { stroke: var(--line); stroke-width: 1; }

/* --------------------------------------------------------------------------
   Login
   -------------------------------------------------------------------------- */
.login-card {
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: 26px;
  box-shadow: var(--shadow);
}
.login-card h1 { font-size: 1.25rem; margin-bottom: 4px; }
.login-card .sub { color: var(--ink-2); font-size: .88rem; margin-bottom: 18px; }

/* --------------------------------------------------------------------------
   Narrow screens. Not a mobile UI — out of scope on 10181 — but a laptop at
   1280 and a sidebar that collapses rather than clipping the table.
   -------------------------------------------------------------------------- */
@media (max-width: 900px) {
  :root { --gutter: 16px; }
  body.app { overflow: auto; }
  .app { flex-direction: column; height: auto; }
  .sidebar, .side {
    width: auto; min-width: 0;
    flex-direction: row; flex-wrap: wrap; align-items: center; gap: 8px;
    border-right: 0; border-bottom: 1px solid var(--line);
    padding: 10px 16px;
  }
  .sidebar-header { padding: 0 8px 0 0; }
  .sidebar-cta { padding: 0; }
  .btn-new { width: auto; }
  .nav { flex: 1 1 100%; display: flex; flex-wrap: wrap; gap: 2px; padding: 0; overflow: visible; }
  .nav-section, .nav-group { display: none; }
  .nav-item.active::before, .nav-item.on::before { display: none; }
  .sidebar-footer, .side-foot { border-top: 0; padding: 0; display: flex; align-items: center; gap: 8px; margin-left: auto; }
  .who { padding: 0; }
  .main { overflow: visible; }
  .content, .wrap { overflow: visible; }
  .topbar { position: sticky; top: 0; z-index: 20; }
  .search-box { max-width: none; }
}

@media (prefers-reduced-motion: reduce) {
  * { animation-duration: .01ms !important; transition-duration: .01ms !important; }
  .btn:hover { transform: none; }
}

/* --------------------------------------------------------------------------
   Print — a run sheet does get printed, and the shell must not come with it.
   -------------------------------------------------------------------------- */
@media print {
  .sidebar, .side, .topbar, .btnbar, .filters { display: none !important; }
  body.app { display: block; overflow: visible; }
  /* The shell wrapper and .main have to be unwound too, or the sheet stops at the first
     page. Resetting only the body left .app on height:100dvh and .main on overflow:hidden,
     so a 498-item run sheet printed as one viewport's worth of rows and the rest was
     clipped with nothing to say it had been (#10388 review round 1). */
  .app, .main { display: block; height: auto; overflow: visible; }
  .content, .wrap { overflow: visible; padding: 0; }
  .card, .tile, table.data { border-color: #bbb; }
}
