/* Loquela Control Center — the admin skin.
   Derived from the public website (website/assets/site.css), which in turn mirrors the
   app's Theme.swift: same palette, radii, gradient tiles and glass idiom, retuned for a
   dense admin tool — tighter type scale, calmer glass over tables, semantic pills in the
   brand colours. Visual spec: mockups/admin-v2/ (approved 2026-08-29).

   Icons are inline SVG with stroke="currentColor" (Web/Icons.cs), so the rules below set
   an icon's colour by setting `color` on whatever contains it. */

:root {
  /* palette — identical to the website */
  --indigo: #5452EB;
  --indigo-d: #3E3CC9;
  --violet: #9E4DF0;
  --teal: #0099A6;
  --mint: #1FBF99;
  --orange: #FA852E;
  --coral: #F5576B;
  --gold: #FABF24;
  --ink: #1D1C2B;
  --muted: #5E5C75;
  --faint: #A9A7BE;
  --bg: #FBFBFE;

  /* glass — a touch more opaque than the site, so tables stay crisp */
  --glass: rgba(255, 255, 255, 0.76);
  --glass-solid: rgba(255, 255, 255, 0.92);
  --glass-border: rgba(255, 255, 255, 0.8);
  --hairline: rgba(29, 28, 58, 0.07);
  --hairline-soft: rgba(29, 28, 58, 0.045);
  --shadow: 0 18px 44px rgba(29, 28, 58, 0.09);
  --shadow-sm: 0 8px 20px rgba(29, 28, 58, 0.07);
}

* { margin: 0; padding: 0; box-sizing: border-box; }

body {
  font-family: -apple-system, BlinkMacSystemFont, "SF Pro Text", "Segoe UI", system-ui, sans-serif;
  color: var(--ink);
  font-size: 15px;
  line-height: 1.5;
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
  min-height: 100vh;
  /* the app's AppBackground, pinned to the viewport and dialled well down —
     a wash behind the work, never competing with it */
  background:
    radial-gradient(46% 34% at 2% 0%,    rgba(84, 82, 235, 0.16) 0%, rgba(84, 82, 235, 0) 100%),
    radial-gradient(40% 30% at 48% -6%,  rgba(158, 77, 240, 0.10) 0%, rgba(158, 77, 240, 0) 100%),
    radial-gradient(42% 32% at 100% 0%,  rgba(0, 153, 166, 0.13) 0%, rgba(0, 153, 166, 0) 100%),
    radial-gradient(40% 34% at 100% 92%, rgba(31, 191, 153, 0.08) 0%, rgba(31, 191, 153, 0) 100%),
    radial-gradient(42% 34% at 0% 100%,  rgba(250, 133, 46, 0.07) 0%, rgba(250, 133, 46, 0) 100%),
    var(--bg);
  background-attachment: fixed;
}

a { color: var(--indigo); text-decoration: none; }
a:hover { color: var(--indigo-d); }

/* **The console's measure, and it is not a reading measure** (D-065, made
   general 2026-09-02).
   This was 1240px — a comfortable width for prose, and wrong for what this
   console actually is. The users list is eight columns; at 1240 the support code
   wrapped across two lines mid-hyphen and the Manage button fell off the right
   edge into a horizontal scroll nobody knew was there. Gelu: *"the user list is
   too small, does not show all columns, including management button."*
   It shipped opt-in, one page at a time. He then asked for it everywhere —
   *"whatever view size u used for users we need that as standard for all our
   admin portal"* — so the opt-in machinery is gone rather than left sitting
   unused: a `ViewData["Wide"]` that every page sets is a flag that says nothing.
   **Prose does not widen with the frame.** `.page-head` keeps 780px and card
   paragraphs cap at 90ch, so a wider console does not mean 1600px sentences —
   that was the whole point of 1240 and it survives as a cap on the text rather
   than on the page. */
.wrap { max-width: min(1680px, 100% - 24px); margin: 0 auto; padding: 0 32px; }
.card p:not(.note) { max-width: 90ch; }

.glass {
  background: var(--glass);
  border: 1px solid var(--glass-border);
  -webkit-backdrop-filter: blur(22px) saturate(1.5);
  backdrop-filter: blur(22px) saturate(1.5);
  box-shadow: var(--shadow);
}

/* ---------- chrome ---------- */
.nav {
  display: flex; align-items: center; gap: 16px;
  padding: 10px 12px 10px 20px;
  border-radius: 24px;
  margin: 20px 0 0 0;
}
.wordmark {
  display: flex; align-items: center; gap: 10px; flex: none;
  font-size: 18px; font-weight: 700; letter-spacing: -0.02em; color: var(--ink);
}
a.wordmark:hover { color: var(--ink); }
.wordmark em { font-style: normal; font-weight: 500; color: var(--muted); }
.mark {
  width: 32px; height: 32px; border-radius: 10px; flex: none;
  background: linear-gradient(135deg, var(--indigo), var(--violet));
  display: flex; align-items: center; justify-content: center;
  box-shadow: 0 6px 14px rgba(84, 82, 235, 0.35);
}
.mark.sm { width: 26px; height: 26px; border-radius: 8px; }
.mark.lg { width: 54px; height: 54px; border-radius: 16px; }
/* The tile IS the ground, so the mark should fill it exactly as it fills the app
   icon. The SVG carries its own 74% inset (tools/make-mark-svgs.py), so letting it
   fill the tile gives 74% of the purple on every surface — matching the home
   screen. Before this it was ~39%: the mark's 74% inside an 18px SVG inside a
   34px tile, two insets multiplied (build-log 107).

   The width/height attributes on the SVG stay as they are — they are the fallback
   if this stylesheet ever fails to load, and CSS overrides them when it does not.
   .mark.icon opts out: Denied.cshtml puts a lock glyph in this tile, and a stroke
   icon at full bleed is not the same design as a mark with a built-in margin. */
.mark > svg { width: 100%; height: 100%; }
.mark.icon > svg { width: auto; height: auto; }


/* One line, always. The links need ~740px and the container gives them ~668,
   so wrapping dropped "Admins" onto a second row. Tightened spacing buys the
   difference; nowrap makes it a rule rather than a hope, and the scroll is the
   safety valve on a genuinely narrow window — a link that scrolls is still
   reachable, a nav that reflows is just untidy. */
.navlinks {
  display: flex; align-items: center; gap: 2px;
  flex: 1 1 auto; min-width: 0;
  flex-wrap: nowrap;
  overflow-x: auto;
  scrollbar-width: none;
}
.navlinks::-webkit-scrollbar { display: none; }
.navlink {
  color: var(--muted); font-size: 14px; font-weight: 500;
  padding: 8px 11px; border-radius: 999px; white-space: nowrap;
  transition: background 0.15s ease, color 0.15s ease;
}
.navlink:hover { color: var(--ink); background: rgba(29, 28, 58, 0.045); }
.navlink.on {
  color: var(--indigo); font-weight: 600;
  background: rgba(84, 82, 235, 0.10);
}
.who { display: flex; align-items: center; gap: 12px; flex: none; font-size: 13.5px; color: var(--muted); }
.who b { color: var(--ink); font-weight: 600; }
.avatar {
  width: 30px; height: 30px; border-radius: 15px; flex: none;
  background: linear-gradient(135deg, var(--teal), var(--mint));
  color: #fff; font-size: 12px; font-weight: 700; letter-spacing: 0.02em;
  display: flex; align-items: center; justify-content: center;
  box-shadow: 0 4px 10px rgba(0, 153, 166, 0.32);
}

main { padding: 34px 0 20px 0; }

footer { padding: 28px 0 44px 0; margin-top: 8px; }
.foot-row {
  display: flex; align-items: center; justify-content: space-between;
  gap: 20px; flex-wrap: wrap;
  border-top: 1px solid var(--hairline); padding-top: 22px;
}
.foot-note { font-size: 13px; color: var(--faint); }
/* The build stamp. Quieter than the note above it and monospaced, because it is
   read character by character when checking whether a deploy landed. */
.build-stamp {
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: 11.5px; opacity: 0.75; letter-spacing: 0.01em;
}

/* ---------- page head ---------- */
.page-head { margin-bottom: 26px; max-width: 780px; }
.eyebrow {
  display: inline-flex; align-items: center; gap: 7px;
  font-size: 11.5px; font-weight: 600; letter-spacing: 0.08em; text-transform: uppercase;
  color: var(--indigo); background: rgba(84, 82, 235, 0.09);
  padding: 6px 12px; border-radius: 999px; margin-bottom: 14px;
}
h1 { font-size: 34px; line-height: 1.1; letter-spacing: -0.03em; font-weight: 800; text-wrap: balance; }
.sub { font-size: 16px; line-height: 1.55; color: var(--muted); margin-top: 10px; text-wrap: pretty; }
.sub b { color: var(--ink); font-weight: 600; }
.head-row { display: flex; align-items: flex-end; justify-content: space-between; gap: 24px; flex-wrap: wrap; margin-bottom: 26px; }
.head-row .page-head { margin-bottom: 0; }
.actions { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }

/* ---------- cards ---------- */
.card {
  border-radius: 24px; padding: 24px 26px; margin-bottom: 20px;
  background: var(--glass);
  border: 1px solid var(--glass-border);
  -webkit-backdrop-filter: blur(22px) saturate(1.5);
  backdrop-filter: blur(22px) saturate(1.5);
  box-shadow: var(--shadow);
}
.card > h2 {
  font-size: 17px; font-weight: 700; letter-spacing: -0.015em;
  display: flex; align-items: center; gap: 10px; margin-bottom: 4px;
}
.card > h2 + .note, .card > h2 + p { margin-top: 8px; }
.card-head { display: flex; align-items: flex-start; justify-content: space-between; gap: 18px; margin-bottom: 16px; }
.card-head h2 { font-size: 17px; font-weight: 700; letter-spacing: -0.015em; }
.card-head .note { margin-top: 5px; }
.card p { font-size: 14.5px; line-height: 1.6; color: var(--muted); }
.card p + p { margin-top: 10px; }
.card p b { color: var(--ink); font-weight: 600; }
.note { font-size: 13px; line-height: 1.55; color: var(--muted); }
.note b { color: var(--ink); font-weight: 600; }
.card > table + .note, .card > .tablewrap + .note { margin-top: 14px; }

/* ---------- KPI tiles ---------- */
.tiles { display: grid; grid-template-columns: repeat(auto-fit, minmax(196px, 1fr)); gap: 18px; margin-bottom: 24px; }
.tile {
  border-radius: 22px; padding: 20px 22px;
  background: var(--glass);
  border: 1px solid var(--glass-border);
  -webkit-backdrop-filter: blur(22px) saturate(1.5);
  backdrop-filter: blur(22px) saturate(1.5);
  box-shadow: var(--shadow-sm);
  display: flex; flex-direction: column; gap: 10px;
}
.tile-tile {
  width: 40px; height: 40px; border-radius: 12px; flex: none;
  display: flex; align-items: center; justify-content: center;
}
.tile .k { font-size: 13px; font-weight: 600; color: var(--muted); }
.tile .v { font-size: 30px; font-weight: 800; letter-spacing: -0.035em; line-height: 1; }
.tile .d { font-size: 12.5px; color: var(--muted); line-height: 1.45; }

/* the website's gradient tile set, reused verbatim */
.t-indigo { background: linear-gradient(135deg, var(--indigo), var(--violet)); box-shadow: 0 8px 16px rgba(84, 82, 235, 0.32); }
.t-teal   { background: linear-gradient(135deg, var(--teal), var(--mint));     box-shadow: 0 8px 16px rgba(0, 153, 166, 0.32); }
.t-mint   { background: linear-gradient(135deg, var(--mint), var(--teal));     box-shadow: 0 8px 16px rgba(31, 191, 153, 0.32); }
.t-orange { background: linear-gradient(135deg, var(--orange), var(--coral));  box-shadow: 0 8px 16px rgba(250, 133, 46, 0.32); }
.t-coral  { background: linear-gradient(135deg, var(--coral), var(--orange));  box-shadow: 0 8px 16px rgba(245, 87, 107, 0.32); }
.t-violet { background: linear-gradient(135deg, var(--violet), var(--coral));  box-shadow: 0 8px 16px rgba(158, 77, 240, 0.32); }
.t-gold   { background: linear-gradient(135deg, var(--gold), var(--orange));   box-shadow: 0 8px 16px rgba(250, 191, 36, 0.35); }
.t-slate  { background: linear-gradient(135deg, #8E8CA8, #5E5C75);             box-shadow: 0 8px 16px rgba(94, 92, 117, 0.28); }

/* ---------- tables ---------- */
.tablewrap { overflow-x: auto; margin: 0 -26px; padding: 0 26px; }
table { width: 100%; border-collapse: collapse; font-size: 14px; }
th {
  text-align: left; font-size: 11.5px; font-weight: 700;
  letter-spacing: 0.07em; text-transform: uppercase; color: var(--faint);
  padding: 0 14px 10px 14px; border-bottom: 1px solid var(--hairline); white-space: nowrap;
}
td { padding: 13px 14px; border-bottom: 1px solid var(--hairline-soft); vertical-align: top; }
tbody tr:last-child td { border-bottom: none; }
tbody tr { transition: background 0.12s ease; }
tbody tr:hover { background: rgba(84, 82, 235, 0.035); }
td:first-child, th:first-child { padding-left: 4px; }
td:last-child, th:last-child { padding-right: 4px; }
.t-num { font-variant-numeric: tabular-nums; }
.t-right { text-align: right; }
.t-nowrap { white-space: nowrap; }
/* **A number broken across two lines is not a number** (D-065). The support
   code is what a household reads off their iPad when they call, so it is the
   one string on this page that has to be copyable at a glance — and it was
   wrapping at its hyphen (`3F9A-` / `2C1E`) whenever the column was squeezed.
   Same for the email under it, which was breaking mid-domain. */
.t-code { white-space: nowrap; }
.sub-cell.t-code { overflow: hidden; text-overflow: ellipsis; max-width: 22ch; }
.primary-cell { font-weight: 700; letter-spacing: -0.01em; font-size: 14.5px; }
.sub-cell { font-size: 12.5px; color: var(--muted); margin-top: 3px; }
.empty { text-align: center; padding: 34px 10px; color: var(--muted); font-size: 14.5px; }

/* ---------- pills ---------- */
.pill {
  display: inline-flex; align-items: center; gap: 6px; white-space: nowrap;
  font-size: 12px; font-weight: 600; letter-spacing: -0.005em;
  padding: 5px 11px; border-radius: 999px; border: 1px solid transparent;
}
.pill.ok   { background: rgba(31, 191, 153, 0.14); color: #0E7C63; border-color: rgba(31, 191, 153, 0.28); }
.pill.warn { background: rgba(250, 133, 46, 0.14); color: #B45A08; border-color: rgba(250, 133, 46, 0.30); }
.pill.bad  { background: rgba(245, 87, 107, 0.13); color: #C42B41; border-color: rgba(245, 87, 107, 0.28); }
.pill.mut  { background: rgba(29, 28, 58, 0.055);  color: var(--muted); border-color: rgba(29, 28, 58, 0.08); }
.pill.info { background: rgba(84, 82, 235, 0.10);  color: var(--indigo); border-color: rgba(84, 82, 235, 0.22); }
.pill.brand { background: linear-gradient(135deg, var(--indigo), var(--violet)); color: #fff; border-color: transparent; }
.dot { width: 6px; height: 6px; border-radius: 3px; background: currentColor; flex: none; }

/* ---------- buttons ---------- */
.btn {
  display: inline-flex; align-items: center; justify-content: center; gap: 8px;
  height: 40px; padding: 0 18px; border-radius: 20px;
  font-size: 14px; font-weight: 600; letter-spacing: -0.01em; font-family: inherit;
  background: linear-gradient(135deg, var(--indigo), var(--violet));
  color: #fff; border: none; cursor: pointer; white-space: nowrap;
  box-shadow: 0 8px 18px rgba(84, 82, 235, 0.30);
  transition: transform 0.15s ease, box-shadow 0.15s ease, background 0.15s ease;
}
.btn:hover { color: #fff; transform: translateY(-1px); box-shadow: 0 12px 24px rgba(84, 82, 235, 0.38); }
.btn.ghost {
  background: rgba(255, 255, 255, 0.72); color: var(--ink);
  border: 1px solid rgba(29, 28, 58, 0.10); box-shadow: none;
  -webkit-backdrop-filter: blur(10px); backdrop-filter: blur(10px);
}
.btn.ghost:hover { color: var(--indigo); border-color: rgba(84, 82, 235, 0.32); transform: none; }
.btn.danger {
  background: rgba(245, 87, 107, 0.09); color: #C42B41;
  border: 1px solid rgba(245, 87, 107, 0.28); box-shadow: none;
}
.btn.danger:hover { background: rgba(245, 87, 107, 0.16); color: #C42B41; transform: none; box-shadow: none; }
.btn.sm { height: 32px; padding: 0 14px; font-size: 13px; border-radius: 16px; }
.btn.lg { height: 48px; padding: 0 24px; font-size: 15.5px; border-radius: 24px; }
.btn.block { width: 100%; }
.btnrow { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }

/* ---------- banners ---------- */
.banner {
  display: flex; align-items: flex-start; gap: 14px;
  border-radius: 20px; padding: 16px 20px; margin-bottom: 22px;
  background: rgba(84, 82, 235, 0.055);
  border: 1px solid rgba(84, 82, 235, 0.16);
}
.banner .btile { width: 34px; height: 34px; border-radius: 11px; flex: none; display: flex; align-items: center; justify-content: center; }
.banner p { font-size: 14px; line-height: 1.55; color: var(--muted); margin: 0; }
.banner p b, .banner strong { color: var(--ink); font-weight: 600; }
.banner.good { background: rgba(31, 191, 153, 0.07); border-color: rgba(31, 191, 153, 0.22); }
.banner.warn { background: rgba(250, 133, 46, 0.07); border-color: rgba(250, 133, 46, 0.24); }
.banner.bad  { background: rgba(245, 87, 107, 0.07); border-color: rgba(245, 87, 107, 0.24); }
.err {
  display: flex; align-items: center; gap: 10px;
  border-radius: 16px; padding: 12px 16px; margin-bottom: 16px;
  background: rgba(245, 87, 107, 0.09); border: 1px solid rgba(245, 87, 107, 0.26);
  color: #C42B41; font-size: 14px; font-weight: 500;
}

/* MVC renders the validation summary unconditionally, tagging it
   `validation-summary-valid` when there is nothing to say. In the old flat style an
   empty one was invisible; `.err` now has a border and a fill, so it must be hidden
   explicitly or every auth page loads showing an empty red box. */
.err.validation-summary-valid { display: none; }
.field-validation-valid { display: none; }

/* ---------- forms (lifted from the site's contact form) ---------- */
.form { display: flex; flex-direction: column; gap: 16px; }
.field { display: flex; flex-direction: column; gap: 7px; }
.field label { font-size: 13px; font-weight: 600; letter-spacing: -0.01em; color: var(--ink); }
.field .hint { font-size: 12.5px; font-weight: 400; color: var(--muted); }
/* Matched by EXCLUSION, not by listing the text-ish types: an <input> with no
   type attribute is a text input to the browser but matched none of the old
   selectors, so it rendered as a raw OS control next to styled siblings. */
input:not([type=checkbox]):not([type=radio]):not([type=file]):not([type=hidden]):not([type=submit]):not([type=button]),
select, textarea {
  font-family: inherit; font-size: 14.5px; color: var(--ink);
  background: rgba(255, 255, 255, 0.88);
  border: 1px solid rgba(29, 28, 58, 0.13); border-radius: 14px;
  padding: 11px 14px; outline: none; width: 100%;
  transition: border-color 0.15s ease, box-shadow 0.15s ease;
}
input:focus, select:focus, textarea:focus { border-color: var(--indigo); box-shadow: 0 0 0 3px rgba(84, 82, 235, 0.15); }
input::placeholder, textarea::placeholder { color: var(--faint); }
textarea { min-height: 110px; resize: vertical; line-height: 1.55; }
select {
  appearance: none; -webkit-appearance: none; padding-right: 38px;
  background-image: url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' viewBox='0 0 24 24' fill='none' stroke='%235E5C75' stroke-width='2.2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M6 9l6 6 6-6'/%3E%3C/svg%3E");
  background-repeat: no-repeat; background-position: right 14px center;
}
.field.narrow input, .field.narrow select { max-width: 220px; }
.field.mid input, .field.mid select { max-width: 420px; }
.formgrid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 16px; }
.check { display: flex; align-items: flex-start; gap: 10px; font-size: 14px; color: var(--muted); line-height: 1.5; }
.check input { width: auto; margin-top: 2px; accent-color: var(--indigo); }
form.inline { display: inline-flex; align-items: center; gap: 8px; }
form.inline input[type=text] { max-width: 170px; height: 32px; padding: 0 12px; font-size: 13px; border-radius: 16px; }

/* ---------- misc ---------- */
.mono { font-family: ui-monospace, "SF Mono", Menlo, monospace; font-size: 12.5px; }
.mono.chip { background: rgba(29, 28, 58, 0.05); border-radius: 7px; padding: 2px 7px; }
.plain { list-style: none; display: flex; flex-direction: column; gap: 9px; }
.plain li { display: flex; align-items: center; gap: 10px; font-size: 14.5px; }
.plain li svg { flex: none; }
.two-col { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 20px; align-items: start; }
.two-col .card { margin-bottom: 0; }
.split { display: grid; grid-template-columns: minmax(0, 1fr) 340px; gap: 20px; align-items: start; }
/* Even halves, for pages where BOTH columns hold a table or a feed. The 340px
   rail is for facts (a handful of label/value rows); a four-column table or an
   audit feed put in it is unreadable and clips its last column. */
.split.even { grid-template-columns: repeat(2, minmax(0, 1fr)); }
/* A checksum, a transaction id or a token has no break opportunity, so in a
   fixed-width track it does not wrap — it overruns the card and widens the
   page. Break those rather than the layout. */
td, .note, .tl-body, .chip, .factrow .v { overflow-wrap: anywhere; }
.split .card { margin-bottom: 20px; }
.split > div > .card:last-child { margin-bottom: 0; }
.factrow { display: flex; align-items: baseline; justify-content: space-between; gap: 16px; padding: 11px 0; border-bottom: 1px solid var(--hairline-soft); }
.factrow:last-child { border-bottom: none; }
.factrow .k { font-size: 13.5px; color: var(--muted); }
.factrow .v { font-size: 14px; font-weight: 600; text-align: right; }
.stack { display: flex; flex-direction: column; gap: 14px; }
.timeline { display: flex; flex-direction: column; gap: 0; }
.tl-item { display: flex; gap: 14px; padding: 13px 0; border-bottom: 1px solid var(--hairline-soft); }
.tl-item:last-child { border-bottom: none; }
.tl-when { font-size: 12.5px; color: var(--muted); width: 128px; flex: none; font-variant-numeric: tabular-nums; }
.tl-body { font-size: 14px; line-height: 1.5; }
.tl-body .who-said { font-weight: 600; }

/* ---------- auth ---------- */
.authpage { min-height: 100vh; display: flex; flex-direction: column; align-items: center; justify-content: center; padding: 40px 20px; }
.authcard {
  width: 100%; max-width: 420px; border-radius: 26px; padding: 34px;
  background: rgba(255, 255, 255, 0.66);
  border: 1px solid var(--glass-border);
  -webkit-backdrop-filter: blur(24px) saturate(1.6); backdrop-filter: blur(24px) saturate(1.6);
  box-shadow: 0 28px 64px rgba(29, 28, 58, 0.14);
}
.authcard .mark { margin-bottom: 20px; }
.authcard h1 { font-size: 28px; letter-spacing: -0.03em; }
.authcard .sub { font-size: 15px; margin-top: 8px; margin-bottom: 24px; }
.authfoot { margin-top: 20px; padding-top: 18px; border-top: 1px solid var(--hairline); }
.code-input { letter-spacing: 0.5em; font-size: 20px !important; font-weight: 700; text-align: center; font-variant-numeric: tabular-nums; }
.auth-note { text-align: center; font-size: 13px; color: var(--faint); margin-top: 22px; }

/* ---------- pager ---------- */
/* Sits under a table, inside the same card. Renders only when there IS another
   page (see _Pager.cshtml), so it never occupies space it cannot use. */
.pager {
  display: flex; align-items: center; justify-content: space-between;
  gap: 16px; flex-wrap: wrap;
  margin-top: 16px; padding-top: 14px; border-top: 1px solid var(--hairline);
}
.pager-count { font-size: 13px; color: var(--muted); font-variant-numeric: tabular-nums; }
.pager-count b { color: var(--ink); font-weight: 600; }
.pager-links { display: flex; align-items: center; gap: 10px; }
.pager-at {
  font-size: 12.5px; color: var(--muted); font-variant-numeric: tabular-nums;
  white-space: nowrap; padding: 0 2px;
}
/* The ends of the range keep their buttons rather than removing them, so the row
   does not reflow as you page — but they must not look clickable. */
.btn.is-off { opacity: 0.38; pointer-events: none; }

/* Grid in the main column, controls in the rail — but the CONTROLS FIRST in the
   DOM, so that when .split collapses to one column below 1000px the filters and
   the search land above the results rather than below a full page of rows. The
   explicit placement is undone in the media query, or a one-column grid would
   still be asked for column 2 and would invent one. */
.split.rail-first > :first-child { grid-column: 2; grid-row: 1; }
.split.rail-first > :last-child  { grid-column: 1; grid-row: 1; }

/* ---------- responsive ---------- */
@media (max-width: 1000px) {
  .split, .two-col { grid-template-columns: minmax(0, 1fr); }
  .formgrid { grid-template-columns: minmax(0, 1fr); }
  /* Back to source order: controls, then results. */
  .split.rail-first > :first-child,
  .split.rail-first > :last-child { grid-column: 1; grid-row: auto; }
}
@media (max-width: 720px) {
  .wrap { padding: 0 18px; }
  h1 { font-size: 28px; }
  .nav { flex-wrap: wrap; }
  .navlinks { order: 3; width: 100%; }   /* below 720px the row is its own line, and scrolls */
  .tablewrap { margin: 0 -20px; padding: 0 20px; }
  .card { padding: 20px; border-radius: 20px; }
}

/* ---------- auth backdrop: the site's mesh at full strength ---------- */
.authbg {
  background:
    radial-gradient(58% 46% at 4% 0%,    rgba(84, 82, 235, 0.30) 0%, rgba(84, 82, 235, 0) 100%),
    radial-gradient(50% 42% at 52% -6%,  rgba(158, 77, 240, 0.18) 0%, rgba(158, 77, 240, 0) 100%),
    radial-gradient(52% 44% at 100% 2%,  rgba(0, 153, 166, 0.22) 0%, rgba(0, 153, 166, 0) 100%),
    radial-gradient(46% 38% at 100% 55%, rgba(31, 191, 153, 0.12) 0%, rgba(31, 191, 153, 0) 100%),
    radial-gradient(50% 40% at 0% 100%,  rgba(250, 133, 46, 0.13) 0%, rgba(250, 133, 46, 0) 100%),
    radial-gradient(52% 42% at 96% 100%, rgba(84, 82, 235, 0.16) 0%, rgba(84, 82, 235, 0) 100%),
    var(--bg);
  background-attachment: fixed;
}

/* ---------- contact sheet ---------- */
a.card { text-decoration: none; transition: transform 0.15s ease, box-shadow 0.15s ease, border-color 0.15s ease; }
a.card:hover { transform: translateY(-2px); box-shadow: 0 22px 52px rgba(29, 28, 58, 0.13); border-color: rgba(84, 82, 235, 0.28); }

/* ---------- icon colours (inline SVG inherits `color` via currentColor) ---------- */
.mark, .tile-tile, .banner .btile { color: #FFFFFF; }
.card > h2 svg, .card-head h2 svg { color: var(--indigo); }
.plain.yes li svg { color: var(--mint); }
.plain.no li svg { color: var(--faint); }

/* ---------- file input ---------- */
input[type=file] {
  font-family: inherit; font-size: 14px; color: var(--muted);
  background: rgba(255, 255, 255, 0.88);
  border: 1px dashed rgba(29, 28, 58, 0.18); border-radius: 14px;
  padding: 14px 16px; width: 100%; max-width: 460px; cursor: pointer;
}
input[type=file]:hover { border-color: rgba(84, 82, 235, 0.42); }
input[type=file]::file-selector-button {
  font-family: inherit; font-size: 13px; font-weight: 600; color: var(--ink);
  background: rgba(255, 255, 255, 0.9); border: 1px solid rgba(29, 28, 58, 0.12);
  border-radius: 16px; padding: 7px 14px; margin-right: 14px; cursor: pointer;
}
input[type=file]::file-selector-button:hover { border-color: rgba(84, 82, 235, 0.4); color: var(--indigo); }
.uploadrow { display: flex; align-items: center; gap: 14px; flex-wrap: wrap; }

/* ---------- diff key lists ---------- */
.keylist { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 14px; }
.keylist .pill { font-family: ui-monospace, "SF Mono", Menlo, monospace; font-size: 12px; font-weight: 500; }
.errlist { display: flex; flex-direction: column; gap: 8px; margin-top: 14px; list-style: none; }
.errlist li {
  font-family: ui-monospace, "SF Mono", Menlo, monospace; font-size: 12.5px; line-height: 1.55;
  background: rgba(29, 28, 58, 0.035); border-radius: 12px; padding: 9px 13px;
}

/* ---------- misc additions ---------- */
.rule { border: none; border-top: 1px solid var(--hairline); margin: 26px 0; }
.secretbox {
  font-family: ui-monospace, "SF Mono", Menlo, monospace; font-size: 14px; word-break: break-all;
  background: rgba(84, 82, 235, 0.06); border: 1px solid rgba(84, 82, 235, 0.16);
  border-radius: 14px; padding: 12px 14px; margin: 12px 0;
}
.secretbox b { font-size: 15px; letter-spacing: 0.04em; }
.otpauth { font-family: ui-monospace, "SF Mono", Menlo, monospace; font-size: 11px; color: var(--faint); word-break: break-all; }
.authcard.wide { max-width: 560px; }
.authcard h2 { font-size: 15px; font-weight: 700; letter-spacing: -0.01em; margin-bottom: 6px; }
.lede { font-size: 15px; line-height: 1.6; color: var(--ink); }
.lede b { font-weight: 700; }
.lede + .lede { margin-top: 12px; }

/* ---------- the timing console (D-096) ----------
   Ported from mockups/telemetry-console/{sessions,session,trends}.html, kept to
   this file's radii, palette vars and hairlines. Everything below is geometry
   the SERVER computes and writes into a style attribute — there is no chart
   library and no JavaScript here, which is the whole point: adding one would be
   this console's first script dependency, for a bar and a thirty-point line. */

/* Sub-navigation within a section. Pill-shaped like .pill, gradient when on,
   so it reads as a place you are rather than a button you press. */
.subtabs { display: flex; gap: 6px; flex-wrap: wrap; margin-bottom: 22px; }
.subtab {
  font-size: 13.5px; font-weight: 600; color: var(--muted);
  padding: 7px 15px; border-radius: 999px; white-space: nowrap;
  border: 1px solid var(--hairline); background: rgba(255, 255, 255, 0.62);
  transition: color 0.15s ease, border-color 0.15s ease;
}
.subtab:hover { color: var(--ink); border-color: rgba(84, 82, 235, 0.26); }
.subtab.on {
  color: #fff; border-color: transparent;
  background: linear-gradient(135deg, var(--indigo), var(--violet));
  box-shadow: 0 6px 14px rgba(84, 82, 235, 0.28);
}

/* ---------- waterfall ---------- */
.wf { margin-top: 6px; min-width: 720px; }
.wf-row {
  display: grid; grid-template-columns: 216px minmax(0, 1fr) 176px;
  align-items: center; gap: 14px;
  padding: 5px 0; border-bottom: 1px solid var(--hairline-soft);
}
.wf-row:last-child { border-bottom: none; }
.wf-row:hover { background: rgba(84, 82, 235, 0.035); }
.wf-row.ruler { border-bottom: 1px solid var(--hairline); padding-bottom: 8px; }
.wf-name {
  font-family: ui-monospace, "SF Mono", Menlo, monospace; font-size: 12px;
  color: var(--ink); white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.wf-name.lvl2 { padding-left: 16px; color: var(--muted); }
.wf-name.lvl3 { padding-left: 32px; color: var(--muted); }
.wf-axis { color: var(--faint); font-size: 11px; letter-spacing: 0.04em; }
/* The faint vertical stripe is the ruler seen through every row, so a bar can
   be read against the clock without tracking back up to the header. */
.wf-track {
  position: relative; height: 20px; border-radius: 6px;
  background-image: repeating-linear-gradient(to right,
    rgba(29, 28, 58, 0.06) 0 1px, rgba(29, 28, 58, 0) 1px 25%);
}
.wf-track.wf-plain { background-image: none; height: 22px; }
.wf-bar { position: absolute; top: 3px; height: 14px; min-width: 3px; border-radius: 7px; }
.wf-bar.wf-full { left: 0; width: 100%; }
.wf-bar.dev,  .stack-bar span.dev,  .bar-cell span.dev  { background: linear-gradient(135deg, var(--indigo), #6E6CF1); }
.wf-bar.srv,  .stack-bar span.srv,  .bar-cell span.srv  { background: linear-gradient(135deg, var(--teal), var(--mint)); }
.wf-bar.ai,   .stack-bar span.ai,   .bar-cell span.ai   { background: linear-gradient(135deg, var(--violet), #BD82F5); }
.wf-bar.der,  .stack-bar span.der,  .bar-cell span.der  { background: rgba(29, 28, 58, 0.10); border: 1px dashed rgba(29, 28, 58, 0.22); }
/* Coral, and a halo, for the one kind of bar whose length was chosen rather
   than observed. It should not sit quietly among the measurements. */
.wf-bar.flag, .stack-bar span.flag, .bar-cell span.flag { background: linear-gradient(135deg, var(--coral), var(--orange)); }
.wf-bar.flag { box-shadow: 0 0 0 3px rgba(245, 87, 107, 0.16); }
.wf-dur { font-variant-numeric: tabular-nums; font-size: 12px; color: var(--muted); text-align: right; white-space: nowrap; }
.wf-dur b { color: var(--ink); font-weight: 700; }
.wf-tick { position: absolute; top: 0; bottom: 0; width: 0; }
.wf-tick i { position: absolute; top: 12px; bottom: 0; left: 0; width: 1px; background: var(--hairline); }
.wf-tick b {
  position: absolute; top: 0; left: 0; transform: translateX(-50%);
  font-size: 11px; font-weight: 600; color: var(--faint); font-variant-numeric: tabular-nums;
}
.wf-tick:first-child b { transform: none; }
/* The last mark sits ON the right edge, so a centred label would be half off it. */
.wf-tick:last-child b { transform: translateX(-100%); }
.wf-note {
  position: absolute; top: 2px; font-size: 11.5px; color: var(--faint);
  white-space: nowrap; pointer-events: none;
}
.wf-note.warn { color: #B45A08; font-weight: 600; }
.wf-rule { height: 1px; background: var(--hairline); margin: 18px 0 10px 0; }

.legend { display: flex; gap: 18px; flex-wrap: wrap; align-items: center; margin-bottom: 14px; }
.legend span { display: inline-flex; align-items: center; gap: 8px; font-size: 12.5px; color: var(--muted); }
.swatch { width: 22px; height: 10px; border-radius: 5px; flex: none; }
.swatch.dev  { background: linear-gradient(135deg, var(--indigo), #6E6CF1); }
.swatch.srv  { background: linear-gradient(135deg, var(--teal), var(--mint)); }
.swatch.ai   { background: linear-gradient(135deg, var(--violet), #BD82F5); }
.swatch.der  { background: rgba(29, 28, 58, 0.10); border: 1px dashed rgba(29, 28, 58, 0.22); }
.swatch.flag { background: linear-gradient(135deg, var(--coral), var(--orange)); }

/* ---------- sparkline and comparison bars ---------- */
.spark { width: 100%; height: 30px; display: block; }
.stack-bar { display: flex; height: 16px; border-radius: 8px; overflow: hidden; margin: 6px 0 2px 0; background: rgba(29, 28, 58, 0.05); }
.stack-bar span { display: block; height: 100%; }
.bar-cell { position: relative; display: block; height: 10px; border-radius: 5px; background: rgba(29, 28, 58, 0.05); min-width: 90px; }
.bar-cell span { position: absolute; left: 0; top: 0; bottom: 0; border-radius: 5px; }

/* ---------- the raw timing pages (D-096 Stage 2) ----------
   A window/endpoint selector made of links rather than a form: the pages stay
   GETs with no JavaScript, and a filtered view can be pasted into a ticket. */
.rawbar {
  display: flex; align-items: center; gap: 8px; flex-wrap: wrap;
  margin: 0 0 18px 0;
}
.rawbar-k { font-size: 11px; letter-spacing: 0.06em; text-transform: uppercase; color: var(--faint); }
.rawbar-sep { width: 1px; height: 16px; background: var(--hairline); margin: 0 6px; }
.chip {
  display: inline-block; padding: 4px 11px; border-radius: 999px;
  border: 1px solid var(--hairline); background: #fff;
  font-size: 12.5px; font-weight: 600; color: var(--muted); text-decoration: none;
  font-variant-numeric: tabular-nums;
}
.chip:hover { border-color: rgba(84, 82, 235, 0.35); color: var(--indigo); }
.chip.on {
  background: rgba(84, 82, 235, 0.10); border-color: rgba(84, 82, 235, 0.30); color: var(--indigo);
}

/* ---------- the drill-down and the step trend (D-096 Stage 3) ----------
   Gelu, 2026-09-05: "general .. but when drill down i see tenants .. and drill
   down tenant i see all sessions .. definitely we need trends per telemetry
   point". Two additions only: a taller chart than a sparkline (two lines that
   have to be read against each other need the height), and the rule that every
   step NAME in this console is a link to its own trend without looking like one
   until you reach for it. Making them look like links would turn the waterfall
   into a page of blue text and lose the shape it exists to show. */
.steptrend {
  width: 100%; height: 90px; display: block;
  margin: 4px 0 10px 0;
  border-bottom: 1px solid var(--hairline);
}
a.wf-name, a.primary-cell, a.mono, a.pill, a.chip.mono { text-decoration: none; color: inherit; }
a.wf-name:hover, a.primary-cell:hover, a.mono:hover { color: var(--indigo); text-decoration: underline; }
a.pill:hover { border-color: rgba(84, 82, 235, 0.35); }
/* A headline step and its two numbers on one chip — a whole row of them stays
   readable only while the numbers are tabular and the name is not wrapped. */
.chip.mono { font-variant-numeric: tabular-nums; margin: 0 4px 4px 0; }

/* A tile that goes somewhere. Same box as `.tile`, but it says so: the cursor
   changes, it lifts on hover, and its caption carries an arrow. A headline
   number with nowhere to go is decoration — Gelu, 2026-09-05, on tapping
   "slowest 8.24 s" and having nothing happen. */
a.tile-link { text-decoration: none; color: inherit; transition: transform .12s ease, box-shadow .12s ease, border-color .12s ease; }
a.tile-link:hover { transform: translateY(-1px); border-color: var(--brand, #5452EB); box-shadow: 0 6px 18px rgba(84, 82, 235, .12); }
a.tile-link:focus-visible { outline: 2px solid var(--brand, #5452EB); outline-offset: 2px; }
a.tile-link .d { color: var(--brand, #5452EB); }

/* A three-state control, drawn as three buttons that submit the same form
   (Telemetry, OQ-43). The one in force is filled rather than ghosted, so the
   row reads as a setting at a glance and not as three things to press. There is
   no radio group and no JavaScript: a radio would need a Save button beside it,
   and a household's telemetry answer is a one-press decision. */
.tri { gap: 0; }
.tri .btn { border-radius: 0; margin-left: -1px; }
.tri .btn:first-of-type { border-radius: 16px 0 0 16px; margin-left: 0; }
.tri .btn:last-of-type { border-radius: 0 16px 16px 0; }
.tri .btn.on {
  background: linear-gradient(135deg, var(--indigo), var(--violet));
  color: #fff; border-color: transparent; position: relative; z-index: 1;
}
.tri .btn.on:hover { color: #fff; transform: none; }

/* ---------- the timing console's filter bar (D-096) ----------
   Gelu, 2026-09-05: "i need for all views date range filters, search by tenant
   .. need quick ways to find what i want". One row of boxes under the chips, on
   every page in the section. Inputs in this console are `width: 100%` by
   default — right in a card, and enough to make each box its own line in a flex
   row — so the bar overrides the width and nothing else. */
.filterbar {
  display: flex; align-items: flex-end; gap: 10px; flex-wrap: wrap;
  margin: 0 0 18px 0;
}
.filterbar .fb { display: flex; flex-direction: column; gap: 4px; }
.filterbar input, .filterbar select {
  width: auto; min-width: 130px; height: 34px;
  padding: 0 12px; font-size: 13px; border-radius: 17px;
}
.filterbar select { padding-right: 34px; background-position: right 12px center; }
.filterbar input[type=number] { min-width: 110px; }
.filterbar .btn { height: 34px; }

/* ---------- the AI console, redesigned (D-119, build-log 219) ----------
   Lifted verbatim from the approved sketch, mockups/ai-console-v2/index.html,
   where these four were the only things invented for the drawing. Everything
   else on those pages is this file's existing vocabulary.

   The number is the point, so it is drawn as one — and it does NOT go away when
   a provider is switched off. The order is a preference and it survives
   availability; "off is not the same as removed" is the promise the whole
   design rests on, and a seat that vanished would break it on the one screen
   that has to show it. */
.seat {
  width: 34px; height: 34px; border-radius: 12px; flex: none;
  display: flex; align-items: center; justify-content: center;
  font-size: 15px; font-weight: 800; color: #fff;
  background: linear-gradient(135deg, var(--indigo), var(--violet));
  box-shadow: 0 6px 14px rgba(84, 82, 235, 0.26);
}
.seat.back { background: linear-gradient(135deg, #8E8CA8, #5E5C75); box-shadow: 0 6px 14px rgba(94, 92, 117, 0.22); }
.seat.asleep { background: rgba(29, 28, 58, 0.10); box-shadow: none; color: var(--faint); }
.seatcell { display: flex; align-items: center; gap: 12px; }

/* **The moves live beside the number, stacked** (Gelu, 2026-09-06: "the screen
   too wide .. need to add prescurtation, or something to make it fit").

   Move up / Move down were the LAST column of the endpoint table, and last is
   the end a too-wide table loses — so the two controls that whole page exists
   for were the only things not on screen. Beside the seat they cannot be cut
   off, and stacked they cost the column one button's width instead of two. */
.movecol { display: flex; flex-direction: column; align-items: stretch; gap: 6px; }
.movecol .btn { justify-content: center; }

/* A <select> is as wide as its LONGEST option, and a model option carries an
   id, a name and two prices — around fifty characters. Left alone it sets the
   width of its column and, with five siblings, of the page. Capped here, with
   the button dropping under it rather than sitting beside it. */
.pickcell form.inline { display: flex; flex-wrap: wrap; max-width: 220px; }
.pickcell select { max-width: 220px; }

/* A time limit is four digits, not the twenty characters an input defaults to. */
form.inline input[type=text].ms { max-width: 92px; }

/* A row that is present, configured, and not being asked. Legible, not hidden:
   everything in it is set up and correct, and an operator has to be able to
   read the model and the deadline of a provider that is waiting for a key. */
.asleep-row td { background: rgba(29, 28, 58, 0.022); }
.asleep-row .primary-cell { color: var(--muted); }

/* A heading and its state pill on one line, wrapping together. */
.provhead { display: flex; align-items: center; gap: 12px; flex-wrap: wrap; }

/* The one chain of cause and effect these pages have to make obvious:
   no key -> cannot be switched on -> so the app skips it -> but it keeps its
   place. Drawn in this shape wherever it applies, in the same words, so nobody
   has to work it out. */
.chain { display: flex; align-items: stretch; gap: 10px; flex-wrap: wrap; margin: 4px 0 0 0; }
.chain .step {
  flex: 1 1 200px; border-radius: 16px; padding: 12px 14px;
  border: 1px solid var(--hairline); background: rgba(255, 255, 255, 0.6);
}
.chain .step b { display: block; font-size: 13.5px; margin-bottom: 3px; }
.chain .step span { font-size: 12.5px; color: var(--muted); line-height: 1.45; }
.chain .step.bad { border-color: rgba(245, 87, 107, 0.3); background: rgba(245, 87, 107, 0.06); }
.chain .arrow { display: flex; align-items: center; color: var(--faint); font-size: 18px; font-weight: 700; }
