/* ==========================================================================
   NVR Cloud Archive — Archieve portal

   Monochrome: black, white and grey only. Removing hue removes it as a
   signal, so state is carried by WEIGHT and FILL instead — a solid dark chip
   is a problem, an outlined one is waiting, plain text is fine — and every
   one still ships its text label. Measured on white: ink 18.9:1, ink-2 9.6:1,
   ink-3 5.3:1, white-on-black 18.9:1.
   ========================================================================== */

/* Open Sans throughout, vendored locally -- no CDN, so the portal renders
   identically offline. Monospace is kept only where character alignment does
   real work: S3 keys and SHA-256 digests. */
@font-face { font-family: "AppSans"; src: url("/static/fonts/opensans-400.woff2") format("woff2"); font-weight: 400; font-display: swap; }
@font-face { font-family: "AppSans"; src: url("/static/fonts/opensans-600.woff2") format("woff2"); font-weight: 600; font-display: swap; }
@font-face { font-family: "AppSans"; src: url("/static/fonts/opensans-700.woff2") format("woff2"); font-weight: 700; font-display: swap; }
@font-face { font-family: "AppMono"; src: url("/static/fonts/mono-400.woff2") format("woff2"); font-weight: 400; font-display: swap; }


:root {
  --plane:      #f8f9fb;   /* page -- lighter than the chrome, darker than a card */
  --plane-2:    #eceef2;   /* rails, deeper ground */
  --surface:    #ffffff;   /* cards */
  --surface-2:  #f7f7f7;   /* inputs, table heads */
  --surface-3:  #ebebeb;   /* hover, chips */

  --ink:        #111111;
  --ink-2:      #454545;
  --ink-3:      #6b6b6b;

  --line:       #e4e4e4;
  --line-2:     #c6c6c6;

  --accent:     #111111;
  --accent-2:   #000000;
  --accent-ink: #ffffff;
  --accent-wash: #e7eefa;

  /* Semantic colour, validated on the white card surface: good 4.9:1,
     warn 5.2:1, bad 6.5:1, accent 5.9:1 -- all clear body-text contrast,
     and each still ships a text label so colour is never the only cue. */
  --good:       #12805a;
  --warn:       #a15c00;
  --bad:        #b3271f;
  --accent-c:   #1f63b8;
  --violet:     #6b4fa8;

  /* Tier hues, CVD-separated as a set on white. */
  --hot:        #c2521f;
  --cool:       #1f63b8;
  --cold:       #12805a;

  /* Lifted for the dark chrome, where the white-surface steps go muddy. */
  --nav-accent: #1f63b8;

  --r:    10px;
  --r-sm: 7px;
  --sb:   244px;
  --hd:   62px;   /* header band height */

  /* Dark chrome. The header band and the sidebar share this surface, so the
     two read as one continuous L rather than two stacked pieces.
     Contrast on #17171a: text 15.3:1, dim 7.5:1, label 6.5:1. */
  /* Light chrome, tinted toward the accent blue so the frame reads as part of
     the UI rather than a slab beside it. Measured on #e8edf5: ink 13.3:1,
     nav text 8.0:1, labels 5.0:1, accent 5.1:1.
     The three planes are ordered chrome -> content -> card, so the most
     tinted surface frames the least. */
  --chrome:      #e8edf5;   /* header band */
  --chrome-2:    #e3e9f3;   /* sidebar -- one step deeper than the header */
  --chrome-line: #d3dce8;
  --chrome-ink:  #1b2430;
  --chrome-dim:  #3e4a5a;
  --chrome-lbl:  #5b6675;
  --chrome-hov:  #dde5f0;
  --chrome-on:   #ffffff;   /* the active nav item inverts to a white pill */
  --sh:   0 1px 2px rgba(0, 0, 0, .06);
  --sh-2: 0 10px 30px rgba(0, 0, 0, .12);

  color-scheme: light;
}

* { box-sizing: border-box; }
html, body { height: 100%; }

body {
  margin: 0;
  background: var(--plane);
  color: var(--ink);
  font: 15px/1.55 "AppSans", ui-sans-serif, system-ui, "Segoe UI", sans-serif;
  letter-spacing: -.002em;
  -webkit-font-smoothing: antialiased;
}

a { color: var(--accent-c); text-decoration: none; }
a:hover { text-decoration: underline; }
code, .mono { font-family: "AppMono", ui-monospace, Consolas, monospace; font-size: .88em; }
.muted { color: var(--ink-3); }
.dim   { color: var(--ink-2); }
.small { font-size: 12.5px; }
.nowrap { white-space: nowrap; }
.right { text-align: right; }

/* ---------- shell ---------- */
.shell { min-height: calc(100vh - var(--hd)); padding-top: var(--hd); }

/* Fixed, not sticky: a sticky column in a flex row only holds while its
   parent is in view, which is why the sidebar tore away on scroll. */
.sidebar {
  position: fixed; top: var(--hd); left: 0;
  width: var(--sb); height: calc(100vh - var(--hd));
  background: var(--chrome-2);
  color: var(--chrome-ink); border-right: 1px solid var(--chrome-line);
  display: flex; flex-direction: column; overflow-y: auto; z-index: 20;
}
/* A thin light edge stops the dark column from bleeding into the page. */
.sidebar::-webkit-scrollbar { width: 8px; }
.sidebar::-webkit-scrollbar-thumb { background: #c3cede; border-radius: 99px; }
.sidebar::-webkit-scrollbar-track { background: transparent; }

/* No divider between brand and the rest of the band -- the chrome is one
   surface, so the sidebar below simply continues it. */
.brand {
  position: relative;
  width: var(--sb); flex: 0 0 var(--sb);
  padding: 0 22px; align-self: stretch;
  display: flex; flex-direction: column; justify-content: center;
}
.brand::before {
  content: ''; position: absolute; left: 0; top: 18px; bottom: 18px;
  width: 3px; border-radius: 0 3px 3px 0; background: var(--nav-accent);
}
.brand b { display: block; font-weight: 600; font-size: 15px; letter-spacing: -.012em; line-height: 1.25; color: var(--chrome-ink); }
.brand span { display: block; font-size: 10px; color: var(--chrome-lbl); letter-spacing: .1em; text-transform: uppercase; margin-top: 3px; }

.nav { padding: 18px 12px 14px; display: flex; flex-direction: column; gap: 3px; flex: 1; }
.nav .lbl { font-size: 9.5px; letter-spacing: .13em; text-transform: uppercase; color: var(--chrome-lbl); padding: 18px 12px 8px; font-weight: 600; }
.nav a {
  position: relative; display: flex; align-items: center; gap: 11px;
  padding: 10px 12px; border-radius: 8px;
  color: var(--chrome-dim); font-size: 14px; font-weight: 600;
  transition: background .14s ease, color .14s ease, transform .14s ease;
}
.nav a:hover { background: var(--chrome-hov); color: var(--chrome-ink); text-decoration: none; }
.nav a:hover svg { opacity: 1; transform: scale(1.06); }
.nav a:focus-visible { outline: 2px solid var(--accent-c); outline-offset: 2px; }
/* A left marker grows in on hover and stays for the current page, so the
   active item is not signalled by shade alone. */
.nav a::before {
  content: ''; position: absolute; left: 0; top: 50%; transform: translateY(-50%);
  width: 3px; height: 0; border-radius: 0 3px 3px 0; background: var(--nav-accent);
  transition: height .16s ease;
}
.nav a:hover::before { height: 16px; }
.nav a.on::before { height: 22px; }
.nav a.on {
  background: var(--chrome-on); color: var(--accent-c);
  box-shadow: 0 1px 2px rgba(27, 36, 48, .10);
}
.nav a.on svg { color: var(--accent-c); opacity: 1; }
.nav a svg { width: 17px; height: 17px; flex: none; color: var(--nav-accent); opacity: .9; transition: opacity .14s ease, transform .14s ease; }
.nav a.on svg { opacity: 1; }
.nav a .n { margin-left: auto; font-size: 11px; padding: 1px 7px; border-radius: 999px; background: var(--accent-c); color: #fff; font-weight: 600; }
.nav a.on .n { background: var(--accent-c); color: #fff; font-weight: 600; }

/* Identity sits at the top right, so it stays on one line -- the name must not
   wrap the way it did stacked in the sidebar. */
.av {
  width: 30px; height: 30px; border-radius: 50%; flex: none;
  background: var(--accent-c); color: #fff;
  display: grid; place-items: center; font-size: 11.5px; font-weight: 700;
  border: 0;
}
.me .id { display: flex; flex-direction: column; line-height: 1.2; min-width: 0; }
.me .who { font-size: 13px; font-weight: 600; white-space: nowrap; color: var(--chrome-ink); }
.me .role { font-size: 10px; letter-spacing: .07em; text-transform: uppercase; color: var(--chrome-lbl); font-weight: 600; }

.main { margin-left: var(--sb); min-width: 0; }
/* Fixed, not sticky. A sticky header only holds while its containing block
   is in view, which is why it scrolled away at the bottom of long pages. */
.topbar {
  position: fixed; top: 0; left: 0; right: 0; z-index: 30;
  display: flex; align-items: center; gap: 12px;
  height: var(--hd); padding-right: 22px;
  background: var(--chrome); color: var(--chrome-ink);
  border-bottom: 1px solid var(--chrome-line);
}
.topbar .chip {
  display: inline-flex; align-items: center; gap: 7px; font-size: 12px; color: var(--ink-2);
  padding: 4px 11px; border: 1px solid var(--line); border-radius: 999px; background: var(--surface-2);
}
.topbar .sp { flex: 1; }
.wrap { padding: 26px 30px 64px; max-width: 1400px; }

/* ---------- headings ---------- */
.head { display: flex; justify-content: space-between; align-items: flex-end; gap: 22px; margin-bottom: 20px; flex-wrap: wrap; }
.head h1 { font-size: 23px; margin: 0 0 5px; letter-spacing: -.02em; font-weight: 700; }
.head h2 { font-size: 17px; margin: 0 0 4px; letter-spacing: -.012em; font-weight: 600; }
.head p { margin: 0; font-size: 13.5px; color: var(--ink-3); max-width: 76ch; }
.sect { margin-top: 34px; }

/* ---------- cards ---------- */
.card { background: var(--surface); border: 1px solid var(--line); border-radius: var(--r); padding: 20px; box-shadow: var(--sh); }
.card + .card { margin-top: 16px; }
/* ...but not inside a grid, where `gap` already does the spacing. The stacking
   margin was pushing the second card down its cell and leaving the pair
   uneven -- side-by-side cards must share one top edge and one height. */
.cols > .card + .card { margin-top: 0; }
.cols > .card { height: 100%; }
.card h3 { margin: 0 0 14px; font-size: 13px; font-weight: 600; letter-spacing: .015em; }
.cols { display: grid; grid-template-columns: repeat(auto-fit, minmax(330px, 1fr)); gap: 16px; }
.empty { text-align: center; padding: 42px 20px; color: var(--ink-3); }
.empty h2 { color: var(--ink); font-size: 16px; margin: 0 0 8px; }
pre { background: var(--surface-2); border: 1px solid var(--line); border-radius: var(--r-sm); padding: 13px 15px; overflow-x: auto; font-size: 12.5px; color: var(--ink-2); margin: 0; }

/* ---------- KPI ---------- */
.kpis { display: grid; grid-template-columns: repeat(auto-fit, minmax(190px, 1fr)); gap: 14px; }
.kpi { background: var(--surface); border: 1px solid var(--line); border-radius: var(--r); padding: 16px 18px; box-shadow: var(--sh); }
.kpi .k { font-size: 10.5px; letter-spacing: .085em; text-transform: uppercase; color: var(--ink-3); margin-bottom: 9px; }
.kpi .v { font-size: 27px; font-weight: 700; letter-spacing: -.03em; line-height: 1.05; font-variant-numeric: tabular-nums; }
.kpi .v .u { font-size: 15px; font-weight: 600; color: var(--ink-2); margin-left: 2px; }
.kpi .t { margin-top: 7px; font-size: 11.5px; color: var(--ink-3); }
/* On target reads plain; off target is underscored so it stays findable in a
   monochrome grid without relying on colour. */
.kpi.good .v { color: var(--good); }
.kpi.warn .v { color: var(--warn); }
.kpi.bad  .v { color: var(--bad); }
.kpi .v.none { color: var(--ink-3); font-size: 18px; font-weight: 600; }

/* ---------- retention rail ---------- */
.rail { height: 6px; border-radius: 999px; background: var(--plane-2); overflow: hidden; }
/* Darker = less time left. Density carries urgency instead of colour. */
.rail i { display: block; height: 100%; border-radius: 999px; background: var(--good); }
.rail.mid i { background: var(--warn); }
.rail.low i { background: var(--bad); }

.expiry { display: flex; align-items: center; gap: 11px; padding: 11px 0; border-bottom: 1px solid var(--line); }
.expiry:last-child { border-bottom: 0; }
.expiry .who { font-size: 13px; }
.expiry .r { margin-left: auto; text-align: right; min-width: 128px; }
.expiry .d { font-variant-numeric: tabular-nums; font-weight: 600; font-size: 13.5px; }

/* ---------- tables ---------- */
.tbl-wrap {
  border: 1px solid var(--line); border-radius: var(--r);
  overflow: hidden; background: var(--surface); box-shadow: var(--sh);
}
.tbl-scroll { overflow-x: auto; max-height: 74vh; overflow-y: auto; }
table { width: 100%; border-collapse: collapse; }
th {
  /* Sticky, so the column meanings stay on screen while a long table scrolls. */
  position: sticky; top: 0; z-index: 2;
  text-align: left; font-size: 10.5px; letter-spacing: .1em; text-transform: uppercase;
  color: var(--ink-3); font-weight: 700; padding: 12px 16px;
  background: var(--surface-2); border-bottom: 1px solid var(--line-2);
  white-space: nowrap;
}
th.num { text-align: right; }
td {
  padding: 13px 16px; border-bottom: 1px solid var(--line);
  vertical-align: middle; font-size: 13.5px;
}
td.num { text-align: right; font-variant-numeric: tabular-nums; }
tbody tr:last-child td { border-bottom: 0; }
/* Zebra banding plus a hover tint and an accent edge, so the eye can track a
   row across many columns without a heavy grid. */
tbody tr:nth-child(even) { background: color-mix(in srgb, var(--surface-2) 55%, var(--surface)); }
tbody tr { transition: background .1s ease, box-shadow .1s ease; }
tbody tr:hover {
  background: var(--accent-wash);
  box-shadow: inset 3px 0 0 var(--accent-c);
}
tbody tr:last-child td { border-bottom: 0; }
.rowlink { cursor: pointer; }
td.act { white-space: nowrap; text-align: right; }
.tight { max-width: 340px; word-break: break-word; color: var(--ink-2); }

/* Audit detail as label/value rows. The stored record is JSON; a wall of
   braces in a table cell is unreadable at a glance, and this column is the
   one an auditor actually reads. Two columns, labels quiet, values in the
   reading font unless they are identifiers that must be compared exactly. */
.kv { display: grid; grid-template-columns: auto minmax(0, 1fr); gap: 3px 14px; margin: 0; max-width: 420px; }
.kv dt {
  font-size: 10.5px; text-transform: uppercase; letter-spacing: .05em;
  color: var(--ink-3); white-space: nowrap; padding-top: 1px;
}
.kv dd { margin: 0; font-size: 12.5px; line-height: 1.45; color: var(--ink-2); word-break: break-word; }
.kv dd.mono { word-break: break-all; }

/* ---------- chips (no colour coding) ---------- */
/* Tier reads as plain text -- no pill. Colour is the secondary cue; the word
   itself is the label. */
.tier-txt { font-weight: 600; font-size: 12.5px; letter-spacing: .01em; color: var(--ink-2); }
.tier-txt.t-HOT  { color: var(--hot); }
.tier-txt.t-COOL { color: var(--cool); }
.tier-txt.t-COLD { color: var(--cold); }

.chip-n {
  display: inline-flex; align-items: center; gap: 6px; padding: 3px 10px;
  border-radius: 999px; font-size: 11px; font-weight: 600; letter-spacing: .02em;
  border: 1px solid var(--line-2); background: var(--surface-2); color: var(--ink-2);
  white-space: nowrap;
}
.chip-n.solid { background: var(--accent-wash); border-color: var(--accent-c); color: var(--accent-2); }
.state { display: inline-flex; align-items: center; font-size: 12px; font-weight: 600; letter-spacing: .01em; }
.state.ok   { color: var(--good); }
.state.wait { color: var(--warn); }
.state.no   { color: var(--bad); }
.tagx { display: inline-block; padding: 2px 8px; border-radius: var(--r-sm); background: var(--surface-2); border: 1px solid var(--line); font-size: 11px; font-family: "AppMono", ui-monospace, Consolas, monospace; color: var(--ink-2); }

/* ---------- filter bar ---------- */
.filters { display: flex; gap: 8px; align-items: center; flex-wrap: wrap; margin-bottom: 16px; }
.seg { display: inline-flex; background: var(--surface-2); border: 1px solid var(--line-2); border-radius: 999px; padding: 3px; gap: 2px; }
.seg a {
  border: 0; background: transparent; color: var(--ink-2); cursor: pointer;
  padding: 6px 15px; border-radius: 999px; font-size: 13px; font-weight: 600;
  box-shadow: none; text-decoration: none; transition: background .12s, color .12s;
  display: inline-flex; align-items: center;
}
.seg a:hover { background: var(--surface-3); color: var(--ink); text-decoration: none; }
.seg a.on { background: var(--accent-c); color: #fff; }
.seg a .c { opacity: .8; font-variant-numeric: tabular-nums; margin-left: 6px; }

.htmx-request .seg a { opacity: .6; }
#catalog.htmx-request { opacity: .55; transition: opacity .12s; }

/* ---------- controls ---------- */
button, .btn, select, input, textarea { font: inherit; }
.btn, button {
  display: inline-flex; align-items: center; justify-content: center; gap: 7px;
  padding: 8px 15px; border-radius: var(--r-sm); border: 1px solid var(--line-2);
  background: var(--surface); color: var(--ink); cursor: pointer; font-size: 13.5px;
  font-weight: 600; box-shadow: var(--sh); transition: background .12s, border-color .12s;
}
.btn:hover, button:hover { background: var(--surface-2); border-color: var(--ink-3); text-decoration: none; }
.btn svg, button svg { width: 15px; height: 15px; }
.btn.pri, button.pri { background: var(--accent-c); border-color: var(--accent-c); color: #fff; }
.btn.pri:hover, button.pri:hover { background: #17518f; border-color: #17518f; }
button.no { background: var(--surface); border-color: var(--bad); color: var(--bad); }
button.no:hover { background: color-mix(in srgb, var(--bad) 10%, var(--surface)); }
.btn.off { opacity: .5; cursor: not-allowed; pointer-events: none; box-shadow: none; }
.btn.sm, button.sm { padding: 5px 11px; font-size: 12.5px; }
button.lnk { background: none; border: 0; color: var(--ink-3); padding: 4px 6px; font-size: 12.5px; text-decoration: underline; box-shadow: none; }
button.lnk:hover { color: var(--ink); background: none; }

input, select, textarea {
  background: var(--surface); border: 1px solid var(--line-2); color: var(--ink);
  border-radius: var(--r-sm); padding: 10px 12px; width: 100%;
  transition: border-color .12s, box-shadow .12s;
}
input::placeholder, textarea::placeholder { color: var(--ink-3); opacity: .85; }
input:focus, select:focus, textarea:focus {
  outline: 0; border-color: var(--accent-c);
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--accent-c) 16%, transparent);
}
textarea { resize: vertical; min-height: 78px; }
label { display: block; margin-bottom: 15px; font-size: 12.5px; color: var(--ink-2); font-weight: 600; }
label input, label textarea, label select { margin-top: 7px; }
.req { color: var(--warn); font-size: 10.5px; letter-spacing: .04em; margin-left: 5px; }
.row { display: flex; gap: 10px; align-items: center; flex-wrap: wrap; }

dl.facts { display: grid; grid-template-columns: 172px 1fr; gap: 10px 20px; font-size: 13.5px; margin: 0; }
dl.facts dt { color: var(--ink-3); }
dl.facts dd { margin: 0; word-break: break-word; }

/* ---------- notices ---------- */
.note { background: var(--surface-2); border-left: 3px solid var(--accent-c); padding: 11px 15px; border-radius: var(--r-sm); font-size: 13px; color: var(--ink-2); margin: 14px 0 0; }
.note.ok { border-left-color: var(--good); }
.note.warn { border-left-color: var(--warn); }
.note.bad { border-left-color: var(--bad); }
.flash { padding: 11px 15px; border-radius: var(--r-sm); margin-bottom: 18px; font-size: 13.5px; border: 1px solid var(--line-2); background: var(--surface); display: flex; gap: 9px; align-items: center; }
.flash.ok { background: color-mix(in srgb, var(--good) 8%, var(--surface)); border-color: var(--good); }
.flash.bad { background: color-mix(in srgb, var(--bad) 8%, var(--surface)); border-color: var(--bad); color: var(--bad); }
.flash.warn { background: color-mix(in srgb, var(--warn) 8%, var(--surface)); border-color: var(--warn); color: var(--warn); }

/* ---------- tickets ---------- */
.tk { border-left: 3px solid var(--line-2); }
.tk.s-READY { border-left-color: var(--good); }
.tk.s-PENDING { border-left-color: var(--warn); }
.tk.s-RESTORING { border-left-color: var(--accent-c); }
.tk.s-REJECTED, .tk.s-EXPIRED { border-left-color: var(--bad); }
.tk-head { display: flex; justify-content: space-between; gap: 16px; align-items: center; flex-wrap: wrap; }
.tk-no { font-weight: 700; font-family: "AppMono", ui-monospace, Consolas, monospace; letter-spacing: .015em; }
.decide { display: flex; gap: 9px; align-items: center; flex-wrap: wrap; margin-top: 14px; }
.decide input { width: 250px; }
.decide select { width: auto; }

video { width: 100%; max-height: 470px; background: #0b0b0b; border-radius: var(--r-sm); display: block; border: 1px solid var(--line-2); }

/* ---------- login ----------
   One centred card on a flat ground. No narrative panel, no gradient -- the
   sign-in page has one job. */
.auth {
  min-height: 100vh;
  display: grid; place-items: center;
  padding: 32px;
  background: var(--chrome-2);
}

.auth-card { width: 100%; max-width: 392px; }

.auth-brand { text-align: center; margin-bottom: 22px; }
.auth-brand b {
  display: block; font-size: 19px; font-weight: 700;
  letter-spacing: -.018em; color: var(--chrome-ink); line-height: 1.2;
}
.auth-brand span {
  display: block; font-size: 10.5px; letter-spacing: .12em; text-transform: uppercase;
  color: var(--chrome-lbl); margin-top: 5px; font-weight: 600;
}

.auth-card .card { padding: 0; border-radius: 14px; box-shadow: var(--sh-2); overflow: hidden; }

.auth-head {
  padding: 24px 28px 20px; border-bottom: 1px solid var(--line); background: var(--surface);
}
.auth-card h2 { font-size: 20px; margin: 0 0 5px; letter-spacing: -.02em; font-weight: 700; }
.auth-card .sub { font-size: 13.5px; color: var(--ink-3); margin: 0; }

.auth-body { padding: 24px 28px 26px; }
.auth-card label { margin-bottom: 16px; font-size: 12px; letter-spacing: .02em; }
.auth-card button[type=submit] {
  width: 100%; padding: 12px; font-size: 14.5px; font-weight: 700; margin-top: 4px;
}
.auth-note {
  font-size: 11.5px; color: var(--ink-3); margin: 0; text-align: center;
  padding: 13px 28px; border-top: 1px solid var(--line); background: var(--surface-2);
}

/* The icon sits inside the field. `.fw` must be a block, or the absolute icon
   centres on the inline box rather than on the input and floats above it. */
.fw { display: block; position: relative; margin-top: 7px; }
.fw input { padding-left: 38px; margin-top: 0; }
.fw .ic {
  position: absolute; left: 13px; top: 50%; transform: translateY(-50%);
  width: 15px; height: 15px; color: var(--ink-3); pointer-events: none;
}

/* ---------- toast ---------- */
.toasts { position: fixed; inset: 0 0 auto 0; z-index: 100; display: flex; justify-content: center; pointer-events: none; }
.toast {
  margin-top: 18px; min-width: 286px; max-width: 470px; background: var(--surface); color: var(--ink);
  border: 1px solid var(--line-2); border-radius: 10px; box-shadow: var(--sh-2); overflow: hidden;
  transform: translateY(-140%); opacity: 0; transition: transform .34s cubic-bezier(.2,.8,.2,1), opacity .24s;
}
.toast.show { transform: translateY(0); opacity: 1; }
.toast .b { display: flex; align-items: center; gap: 11px; padding: 12px 16px; font-size: 13.5px; }
.toast .ic { width: 20px; height: 20px; border-radius: 50%; display: grid; place-items: center; flex: none; }
.toast .ic svg { width: 12px; height: 12px; }
.toast .m b { display: block; font-weight: 600; }
.toast .m span { color: var(--ink-3); font-size: 12px; }
.toast .rail { height: 3px; background: var(--surface-3); }
.toast .rail i { display: block; height: 100%; width: 0; background: var(--accent-c); transition: width .9s linear; }
.toast.run .rail i { width: 100%; }
.toast.ok .ic { background: var(--accent-wash); color: var(--accent-c); }
.toast.err .ic { background: var(--ink); color: var(--surface); }
.toast.err .rail i { background: var(--ink); }

@media (prefers-reduced-motion: reduce) {
  .toast { transition: opacity .2s; transform: none; }
  .toast .rail i { transition: none; }
}

@media (max-height: 560px) { .auth { height: auto; min-height: 100vh; overflow: auto; } }
@media (max-width: 900px) {
  .shell { flex-direction: column; }
  .sidebar { width: 100%; flex: none; height: auto; position: static; }
  .nav { flex-direction: row; flex-wrap: wrap; flex: none; }
  .nav .lbl { display: none; }
  .wrap { padding-left: 18px; padding-right: 18px; }
  .topbar { padding-right: 14px; }
  .brand { width: auto; flex: 0 0 auto; padding: 0 14px; }
  .topbar .chip { display: none; }
  .me .id { display: none; }
  dl.facts { grid-template-columns: 1fr; gap: 4px 0; }
  dl.facts dd { margin-bottom: 10px; }
}

/* ==========================================================================
   profile menu  ·  compact KPI strip  ·  charts
   Colour appears only in data. Chrome stays black / white / grey.
   ========================================================================== */

/* ---------- profile dropdown ---------- */
/* Keep <details> as display:block. Overriding it (e.g. to flex) defeats the
   UA rule that hides non-summary children while closed, leaving the menu
   permanently on screen. The summary is the flex row instead. */
/* <details> must keep display:block. Overriding it (e.g. to flex) defeats the
   UA rule that hides non-summary children while closed, leaving the menu
   permanently on screen. The summary is the flex row instead. */
.me { display: block; position: relative; }
.me > summary {
  display: flex; align-items: center; gap: 10px; cursor: pointer;
  padding: 5px 10px 5px 5px; border-radius: 999px; list-style: none;
  border: 1px solid var(--chrome-line); transition: background .14s, border-color .14s;
}
.me > summary::-webkit-details-marker { display: none; }
.me > summary:hover { background: var(--chrome-hov); border-color: var(--chrome-line); }
.me[open] > summary { background: var(--chrome-on); border-color: var(--chrome-line); box-shadow: 0 1px 2px rgba(27, 36, 48, .10); }
.me .caret { width: 14px; height: 14px; color: var(--chrome-lbl); transition: transform .15s; }
.me[open] .caret { transform: rotate(180deg); }

/* A light popover hanging off dark chrome: every colour is restated, or the
   text inherits the chrome's white ink and disappears against the card. */
.menu {
  color: var(--ink);
  position: absolute; top: calc(100% + 8px); right: 0; z-index: 40;
  min-width: 250px; background: var(--surface);
  border: 1px solid var(--line-2); border-radius: var(--r);
  box-shadow: var(--sh-2); padding: 6px; overflow: hidden;
}
.menu-head { display: flex; align-items: center; gap: 11px; padding: 11px 11px 13px; border-bottom: 1px solid var(--line); margin-bottom: 6px; }
.menu-head b { display: block; font-size: 13.5px; font-weight: 600; color: var(--ink); }
.menu-head .small { display: block; color: var(--ink-3); }
.av.lg { width: 38px; height: 38px; font-size: 13px; background: var(--accent-c); color: #fff; border-color: transparent; }
.menu a, .menu button {
  display: flex; align-items: center; gap: 10px; width: 100%;
  padding: 9px 11px; border-radius: var(--r-sm); font-size: 13.5px;
  background: none; border: 0; box-shadow: none; color: var(--ink);
  text-align: left; cursor: pointer; text-decoration: none; font-weight: 400;
}
.menu a:hover, .menu button:hover { background: var(--surface-2); text-decoration: none; }
.menu svg { width: 16px; height: 16px; color: var(--accent-c); flex: none; }
.menu form { margin: 0; }

/* ---------- compact KPI strip ---------- */
.strip {
  display: flex; background: var(--surface); border: 1px solid var(--line);
  border-radius: var(--r); box-shadow: var(--sh); overflow: hidden;
}
.s-item {
  flex: 1 1 0; min-width: 0;
  padding: 15px 20px; border-right: 1px solid var(--line);
}
.s-item:last-child { border-right: 0; }
.s-k { display: block; font-size: 10px; letter-spacing: .09em; text-transform: uppercase; color: var(--ink-3); font-weight: 600; }
/* The number itself carries the colour -- it is the thing being read. */
.s-v {
  display: block; font-size: 25px; font-weight: 700; letter-spacing: -.03em;
  line-height: 1.15; margin-top: 4px; font-variant-numeric: tabular-nums;
  color: var(--c, var(--ink));
}
.s-v u { font-size: 14px; font-weight: 600; color: inherit; opacity: .72; text-decoration: none; margin-left: 1px; }
.s-t { display: block; font-size: 11.5px; color: var(--ink-3); margin-top: 3px; }

/* ---------- charts ---------- */
.c-head { display: flex; justify-content: space-between; align-items: baseline; gap: 12px; margin-bottom: 16px; flex-wrap: wrap; }
.c-head h3 { margin: 0; margin-right: auto; }

.range-toggle { display: inline-flex; gap: 6px; }
.range-toggle .btn.on, .range-toggle .btn.on:hover { background: var(--accent-c); border-color: var(--accent-c); color: #fff; }

.chart { display: flex; align-items: flex-end; gap: 5px; height: 132px; }
.chart-col { flex: 1 1 0; display: flex; flex-direction: column; align-items: center; height: 100%; min-width: 0; }
.chart-val { font-size: 10.5px; color: var(--ink-2); font-variant-numeric: tabular-nums; height: 14px; font-weight: 600; }
.chart-fill { width: 100%; max-width: 26px; background: var(--c); border-radius: 3px 3px 0 0; margin-top: auto; transition: opacity .12s; }
.chart-col:hover .chart-fill { opacity: .78; }
.chart-lbl { font-size: 10px; color: var(--ink-3); margin-top: 6px; font-variant-numeric: tabular-nums; }

/* 2px gaps between segments so adjacent fills never touch. */
.stack { display: flex; gap: 2px; height: 12px; border-radius: 3px; overflow: hidden; margin-bottom: 14px; }
.stack span { display: block; }
.stack.sm { height: 8px; margin-bottom: 5px; }

table.mini td { padding: 6px 10px 6px 0; border: 0; font-size: 13px; }
table.mini tbody tr:hover { background: transparent; }
td.sw, .sw { width: 18px; padding-right: 0; }
.sw i { display: block; width: 10px; height: 10px; border-radius: 3px; }
td.num { font-variant-numeric: tabular-nums; }

.perms { list-style: none; margin: 0; padding: 0; display: grid; gap: 7px; }
.perms li code {
  display: inline-block; background: var(--surface-2); border: 1px solid var(--line);
  padding: 3px 9px; border-radius: var(--r-sm); color: var(--ink-2);
}

@media (max-width: 1100px) {
  .strip { flex-wrap: wrap; }
  .s-item { flex: 1 1 210px; border-bottom: 1px solid var(--line); }
}

/* ---------- typography ----------
   One family throughout. Open Sans ships 400 / 600 / 700 here, so nothing
   asks the browser to synthesise a weight it does not have. */
.head h1, .head h2, .card h3, .c-head h3,
.head h1 { font-size: 24px; font-weight: 700; }
.head h2 { font-size: 17px; font-weight: 700; }
.s-v, .kpi .v { font-family: "AppSans", system-ui, sans-serif; font-weight: 700; }
.card h3, .c-head h3 { font-weight: 700; font-size: 13.5px; letter-spacing: 0; }
.brand b { font-weight: 700; }

/* ---------- pagination ---------- */
.pager {
  display: flex; align-items: center; justify-content: space-between; gap: 16px;
  padding: 12px 4px 0; flex-wrap: wrap;
}
.pg-links { display: inline-flex; align-items: center; gap: 6px; }
.pg-links a {
  padding: 6px 13px; border-radius: var(--r-sm); border: 1px solid var(--line-2);
  background: var(--surface); color: var(--accent-c); font-size: 13px; font-weight: 600;
  text-decoration: none; transition: background .12s, border-color .12s;
}
.pg-links a:hover { background: var(--accent-wash); border-color: var(--accent-c); text-decoration: none; }
/* A disabled control must not be reachable, not merely look faded. */
.pg-links a.off { opacity: .4; pointer-events: none; color: var(--ink-3); }
.pg-links .muted { padding: 0 6px; }

/* ---------- table polish ---------- */
.tbl-wrap table { border-collapse: separate; border-spacing: 0; }
.tbl-wrap + .pager { padding-top: 14px; }

/* A caption strip above the rows: count on one side, controls on the other. */
.tbl-head {
  display: flex; align-items: center; justify-content: space-between; gap: 14px;
  padding: 13px 16px; border-bottom: 1px solid var(--line);
  background: var(--surface);
}
.tbl-head h3 { margin: 0; }

/* Keep monospace object keys from forcing a wide table. */
td .mono { word-break: break-all; }
td a.mono { font-weight: 600; }

/* The first column carries the row's identity, so it reads a shade stronger. */
tbody td:first-child { color: var(--ink); }

/* Compact action column: buttons never stretch the row. */
td.act { white-space: nowrap; text-align: right; vertical-align: middle; }
td.act .btn { margin-left: 6px; }

/* Inline decision form inside a table cell. */
td.act .decide { display: inline-flex; gap: 6px; align-items: center; margin: 0; flex-wrap: nowrap; }
td.act .decide input { width: 130px; }
td.act .decide select { width: auto; }

/* ---------- line chart ---------- */
/* Uniformly scaled: no fixed pixel height and no preserveAspectRatio override,
   so strokes stay even and markers stay round at any container width. */
.linechart { width: 100%; height: auto; display: block; overflow: visible; }
/* Hairline, solid. A dashed grid reads as a threshold when it is only a scale. */
.linechart .grid { stroke: var(--line); stroke-width: 1; }
.linechart .grid.zero { stroke: var(--line-2); }
.linechart .axis {
  font-size: 12px; fill: var(--ink-3);
  font-family: "AppSans", system-ui, sans-serif; font-variant-numeric: tabular-nums;
}
.linechart .axis.num { text-anchor: end; }
.linechart .axis.mid { text-anchor: middle; }
.linechart .axis.on { fill: var(--ink); font-weight: 700; }
/* One direct label, in a text token -- the mark carries the colour, not the type. */
.linechart .pt-label {
  font-size: 14px; font-weight: 700; fill: var(--ink); text-anchor: middle;
  font-family: "AppSans", system-ui, sans-serif; font-variant-numeric: tabular-nums;
  paint-order: stroke; stroke: var(--surface); stroke-width: 3px; stroke-linejoin: round;
}
.linechart .pt-label.peak { fill: var(--ink-3); font-weight: 600; }
.linechart .dot { transition: r .12s ease; }
.linechart .colsel { fill: var(--accent-c); fill-opacity: .07; }

/* ---------- donut ---------- */
.donut-wrap { display: flex; align-items: center; gap: 22px; flex-wrap: wrap; }
.donut { width: 140px; height: 140px; flex: none; }
.donut path { transition: opacity .12s ease; cursor: default; }
.donut:hover path { opacity: .55; }
.donut path:hover { opacity: 1; }
.donut-n {
  text-anchor: middle; font-size: 24px; font-weight: 700; fill: var(--ink);
  font-family: "AppSans", system-ui, sans-serif; font-variant-numeric: tabular-nums;
}
.donut-l {
  text-anchor: middle; font-size: 10px; fill: var(--ink-3);
  font-family: "AppSans", system-ui, sans-serif; letter-spacing: .06em; text-transform: uppercase;
}
.donut-wrap .mini { flex: 1 1 200px; }

/* ---------- structured form panel ---------- */
.reqbox { padding: 0; overflow: hidden; }
.reqbox-head {
  display: flex; align-items: baseline; justify-content: space-between; gap: 14px;
  padding: 16px 22px; border-bottom: 1px solid var(--line); background: var(--surface-2);
}
.reqbox-head h3 { margin: 0; }
.reqbox-body { padding: 22px; }
.reqbox-body label { margin-bottom: 20px; }
.reqbox-body label:last-child { margin-bottom: 0; }
.reqbox-foot {
  display: flex; align-items: center; justify-content: space-between; gap: 16px;
  padding: 16px 22px; border-top: 1px solid var(--line); background: var(--surface-2);
  flex-wrap: wrap;
}
.reqbox-foot p { margin: 0; }

/* A hint under a field explains the consequence, not the format. */
.hint {
  display: block; margin-top: 6px; font-size: 11.5px; color: var(--ink-3);
  font-weight: 400; line-height: 1.45;
}

.grid-2col { display: grid; grid-template-columns: repeat(auto-fit, minmax(240px, 1fr)); gap: 0 20px; }

/* Role reference, so the choice is not made blind. */
.rolekey {
  display: grid; gap: 8px; margin-top: 4px; padding: 14px 16px;
  background: var(--surface-2); border: 1px solid var(--line); border-radius: var(--r-sm);
  font-size: 12.5px;
}
.rolekey div { display: flex; gap: 10px; align-items: baseline; }
.rolekey strong { min-width: 96px; font-weight: 700; }

/* ---------- the strip as a set of drill-down links ----------
   Each tile opens the rows behind its number, so it has to read as pressable
   without becoming a button: the affordance is a hover ground and a rule under
   the active one, never a border that would shift the layout on click. */
a.s-item { color: inherit; text-decoration: none; cursor: pointer; position: relative; }
a.s-item::after {
  content: ""; position: absolute; left: 0; right: 0; bottom: 0; height: 3px;
  background: var(--accent-c); opacity: 0; transition: opacity .12s ease;
}
a.s-item:hover { background: var(--surface-2); text-decoration: none; }
a.s-item:hover::after { opacity: .35; }
a.s-item.on { background: var(--surface-2); }
a.s-item.on::after { opacity: 1; }
a.s-item:focus-visible { outline: 2px solid var(--accent-c); outline-offset: -2px; }

/* The open panel is tied to the strip above it rather than floating free. */
.card.panel { margin-top: 14px; }
.card.panel .c-head { align-items: flex-start; }
.card.panel .c-head h3 { margin-bottom: 3px; }
.p-act { display: flex; align-items: center; gap: 12px; flex: none; }

/* ---------- day picker on the line chart ----------
   A 4px marker is not a click target, so each point owns a transparent column
   the full height of the chart. */
.linechart.pick .daypick { cursor: pointer; }
.linechart.pick .hit { fill: transparent; }
.linechart.pick .daypick:hover .hit { fill: var(--ink); fill-opacity: .04; }
.linechart.pick .daypick:hover .dot { r: 6; }
.linechart.pick .daypick:focus-visible .hit { fill: var(--accent-c); fill-opacity: .12; }

/* ---------- hour strip ---------- */
.tl-head {
  display: flex; align-items: center; justify-content: space-between;
  gap: 14px; flex-wrap: wrap; margin: 18px 0 10px;
}
.tl-head.sel {
  margin-top: 18px; padding-top: 14px; border-top: 1px solid var(--line);
  font-size: 13.5px;
}
.daynav { display: flex; align-items: center; gap: 10px; }
.daynav strong { font-size: 14px; font-variant-numeric: tabular-nums; }
.daynav .btn { line-height: 1; padding: 4px 10px; font-size: 15px; }

/* The bars are measured from one baseline rule, drawn under the plot and above
   the hour labels -- without it the columns float and an empty day reads as a
   rendering failure rather than as zero. */
.hours { position: relative; display: flex; align-items: stretch; gap: 2px; height: 128px; }
.hours::after {
  content: ""; position: absolute; left: 0; right: 0; bottom: 17px; height: 1px;
  background: var(--line-2);
}
.hours .hr {
  flex: 1 1 0; min-width: 0; display: flex; flex-direction: column;
  justify-content: flex-end; align-items: center; gap: 4px;
  padding: 2px 1px 0; border-radius: var(--r-sm) var(--r-sm) 0 0;
  text-decoration: none; color: inherit; cursor: pointer;
  transition: background .12s ease;
}
.hours .hr:hover { background: var(--surface-2); text-decoration: none; }
.hours .hr:focus-visible { outline: 2px solid var(--accent-c); outline-offset: -2px; }
/* The count sits above its own bar -- a legend would make the reader look away
   from the thing they are comparing. */
.hours .hr .n {
  font-size: 11px; font-weight: 700; color: var(--ink-2); line-height: 1;
  font-variant-numeric: tabular-nums; height: 12px;
}
/* The column is the full plot height; the bar grows inside it from the baseline,
   so every bar shares one origin however tall it is. */
.hours .hr .col {
  flex: 1; width: 100%; display: flex; align-items: flex-end; justify-content: center;
  min-height: 0;
}
.hours .hr i {
  display: block; width: 100%; max-width: 22px; border-radius: 4px 4px 0 0;
  background: linear-gradient(180deg,
              color-mix(in srgb, var(--accent-c) 92%, #ffffff) 0%,
              color-mix(in srgb, var(--accent-c) 74%, #ffffff) 100%);
  transition: opacity .12s ease, filter .12s ease;
}
/* An empty hour gets air, not a stub. It keeps its hit area and its label. */
.hours .hr .t {
  font-size: 10px; color: var(--ink-3); line-height: 1; height: 13px; padding-top: 4px;
  font-variant-numeric: tabular-nums;
}
.hours:hover .hr i { opacity: .45; }
.hours .hr:hover i { opacity: 1; }
.hours .hr.on { background: var(--accent-wash); }
.hours .hr.on i { opacity: 1; filter: saturate(1.1); }
.hours .hr.on .n, .hours .hr.on .t { color: var(--ink); font-weight: 700; }

.htmx-request .hours, .htmx-request.card { opacity: .55; }

@media (max-width: 720px) {
  .hours { height: 100px; }
  .hours .hr .n { display: none; }
  .p-act { width: 100%; justify-content: space-between; }
}

/* ---------- grouped form sections ----------
   The add-user form is the page's subject, so it runs full width and lets the
   fields flow into as many columns as the viewport affords. Grouping them under
   legends is what keeps a 12-field form readable: the reader scans four short
   groups rather than one long list. */
.fset {
  border: 0; border-top: 1px solid var(--line); margin: 0; padding: 20px 0 4px;
}
.fset:first-of-type { border-top: 0; padding-top: 0; }
.fset > legend {
  padding: 0 0 14px; font-size: 11px; font-weight: 700; letter-spacing: .1em;
  text-transform: uppercase; color: var(--ink-3);
}
.fgrid { display: grid; grid-template-columns: repeat(auto-fit, minmax(250px, 1fr)); gap: 0 22px; }
.fgrid.narrow { grid-template-columns: minmax(220px, 320px); }
.fgrid > label.plain { margin-bottom: 14px; }
.reqbox-body textarea { resize: vertical; min-height: 58px; }

/* A checkbox reads as a decision, not a field: it sits on the baseline of its
   own explanation rather than under a label like the text inputs. */
.check {
  display: flex; align-items: flex-start; gap: 10px; margin: 2px 0 6px !important;
  padding: 12px 14px; background: var(--surface-2); border: 1px solid var(--line);
  border-radius: var(--r-sm); font-weight: 400;
}
.check input { width: 16px; height: 16px; margin: 1px 0 0; flex: none; accent-color: var(--accent-c); }
.check b { display: block; font-size: 13px; font-weight: 600; color: var(--ink); }
.check .hint { margin-top: 3px; }
.state.small { font-size: 11px; }
