/*
 * ONYX support console.
 *
 * The visual language follows the customer app: a dark graphite/navy base,
 * electric cyan as the single primary accent, amber for anything that asks an
 * operator to act, uppercase technical headings, and layered rounded cards.
 * What it deliberately does *not* borrow is the app's density. This is an admin
 * surface: tables stay tight, type stays small, and nothing is spaced out for
 * atmosphere at the cost of rows on screen.
 *
 * No external asset, font, or stylesheet is referenced from this file. Every
 * font is a system stack, every shape is CSS, and the served CSP is
 * `default-src 'none'; script-src 'self'; style-src 'self'` — a webfont or an
 * icon sprite from anywhere else would be blocked, and would also be a third
 * party learning which operator opened a support console.
 *
 * COLOUR IS NEVER THE ONLY SIGNAL. Every state that is coloured also carries
 * its own words, because ONYX has published no verified meaning for a
 * controller code and a colour alone would be this console inventing a severity.
 */

:root {
  color-scheme: dark;

  /* Base: graphite with a navy cast, layered rather than flat. */
  --bg: #07090d;
  --bg-glow: #0d1420;
  --surface: #10141c;
  --surface-2: #161c26;
  --surface-3: #1d2532;
  --line: #242c3a;
  --line-strong: #3a4658;

  --text: #eef1f6;
  --text-dim: #c3ccda;
  --muted: #8e9bb0;

  /* Electric cyan is the primary and the only "go" colour. */
  --primary: #04f8e2;
  --primary-dim: #14b8ab;
  --primary-ink: #05201f;
  --primary-wash: #04f8e21f;

  /* Amber means "a human should look at this". It is never decorative. */
  --warn: #fbcd0a;
  --warn-wash: #fbcd0a1a;

  /* Reserved for destructive operator actions, not for device state. */
  --danger: #ff6b81;
  --danger-wash: #ff6b811a;

  --ok: #3ddc97;
  --unknown: #8e9bb0;

  --radius: 14px;
  --radius-sm: 9px;
  --radius-pill: 999px;

  --sans: "Segoe UI", system-ui, -apple-system, "Helvetica Neue", Arial, sans-serif;
  /* Specs, versions, digests, and raw controller codes. All system fonts. */
  --mono: ui-monospace, "SF Mono", SFMono-Regular, "Cascadia Mono", Menlo, Consolas, "Liberation Mono", monospace;
}

* { box-sizing: border-box; }

/*
 * Author-level display rules such as `.tabs { display: flex }` otherwise beat
 * the browser's user-agent `[hidden]` rule — same specificity, later in the
 * file, so the tabs would lay out while still marked hidden. Keep every
 * privileged surface absent from layout until app.js removes `hidden` after the
 * server has authorized the operator.
 *
 * This must remain the ONLY `!important` declaration in this stylesheet. A
 * second one on `display` could out-specify it and quietly bring a privileged
 * surface back onto a logged-out page; a test asserts there is exactly one and
 * that it belongs to this rule.
 */
[hidden] { display: none !important; }

body {
  margin: 0;
  background:
    radial-gradient(1100px 520px at 12% -12%, var(--bg-glow), transparent 62%),
    radial-gradient(900px 460px at 105% 0%, #0a1a1d, transparent 58%),
    var(--bg);
  background-attachment: fixed;
  color: var(--text);
  font: 15px/1.5 var(--sans);
  -webkit-text-size-adjust: 100%;
}

/* --- masthead ------------------------------------------------------------ */

.top {
  display: flex;
  flex-wrap: wrap;
  gap: 12px;
  align-items: center;
  justify-content: space-between;
  padding: 13px 20px;
  border-bottom: 1px solid var(--line);
  background: linear-gradient(180deg, #121722, #0a0d13);
  position: sticky;
  top: 0;
  z-index: 3;
}
/* The cyan hairline that ties the console to the app's chrome. */
.top::after {
  content: "";
  position: absolute;
  left: 0;
  right: 0;
  bottom: -1px;
  height: 1px;
  background: linear-gradient(90deg, transparent, var(--primary), transparent);
  opacity: 0.55;
}

.brand { display: flex; align-items: baseline; gap: 10px; }
.brand-mark {
  font-weight: 800;
  font-size: 19px;
  letter-spacing: 0.26em;
  color: var(--primary);
  text-transform: uppercase;
}
.brand-sub {
  color: var(--muted);
  font-size: 12px;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  font-weight: 600;
}
.session { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; font-size: 13px; }
.session-subject { font-weight: 600; font-family: var(--mono); font-size: 12.5px; }
.session-roles { color: var(--muted); text-transform: uppercase; letter-spacing: 0.08em; font-size: 11.5px; }

main { padding: 20px; max-width: 1440px; margin: 0 auto; display: flex; flex-direction: column; gap: 16px; }

/* --- layered cards -------------------------------------------------------- */

.card {
  position: relative;
  background: linear-gradient(180deg, var(--surface-2), var(--surface));
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: 16px 18px;
  box-shadow: 0 1px 0 #ffffff08 inset, 0 10px 26px #00000045;
}
/* The second layer: a short accent rule at the card's shoulder. */
.card::before {
  content: "";
  position: absolute;
  top: -1px;
  left: 18px;
  width: 54px;
  height: 2px;
  border-radius: var(--radius-pill);
  background: var(--primary);
  opacity: 0.7;
}
.card.plain::before { display: none; }

/* --- technical type hierarchy -------------------------------------------- */

h1 {
  font-size: 19px;
  margin: 0 0 8px;
  text-transform: uppercase;
  letter-spacing: 0.11em;
  font-weight: 800;
}
h2 {
  font-size: 14px;
  margin: 0 0 12px;
  text-transform: uppercase;
  letter-spacing: 0.13em;
  font-weight: 700;
  color: var(--text);
}
h3 {
  font-size: 11.5px;
  margin: 22px 0 8px;
  text-transform: uppercase;
  letter-spacing: 0.15em;
  color: var(--muted);
  font-weight: 700;
  padding-bottom: 6px;
  border-bottom: 1px solid var(--line);
}
.muted { color: var(--muted); }
.small { font-size: 12.5px; }
.mono { font-family: var(--mono); }

/* --- state blocks: empty, error, loading ---------------------------------- */

/*
 * All three say what happened and what to do next. "No results" on its own
 * reads as "this customer has no bike", which is a different and much worse
 * claim than "nothing matched what you typed".
 */
.empty, .loading, .error {
  margin: 12px 0 0;
  padding: 14px 16px;
  border-radius: var(--radius-sm);
  border: 1px dashed var(--line);
  background: #0d1219a8;
  color: var(--muted);
  font-size: 13px;
  line-height: 1.55;
}
.empty::before, .loading::before, .error::before {
  display: block;
  font-size: 10.5px;
  font-weight: 700;
  letter-spacing: 0.17em;
  text-transform: uppercase;
  margin-bottom: 5px;
}
.empty::before { content: "No results"; color: var(--muted); }
.loading::before { content: "Loading"; color: var(--primary); }
.error {
  border-style: solid;
  border-color: var(--danger);
  background: var(--danger-wash);
  color: var(--text);
}
.error::before { content: "Request failed"; color: var(--danger); }

/* A determinate-looking bar for an indeterminate wait: honest, and it moves. */
.loading::after {
  content: "";
  display: block;
  margin-top: 9px;
  height: 2px;
  border-radius: var(--radius-pill);
  background: linear-gradient(90deg, transparent, var(--primary), transparent);
  background-size: 40% 100%;
  background-repeat: no-repeat;
  animation: sweep 1.15s linear infinite;
}
@keyframes sweep {
  from { background-position: -45% 0; }
  to { background-position: 145% 0; }
}
@media (prefers-reduced-motion: reduce) {
  .loading::after { animation: none; background-position: 30% 0; }
}

/* --- forms and controls --------------------------------------------------- */

label {
  display: block;
  font-size: 11px;
  color: var(--muted);
  margin-bottom: 6px;
  text-transform: uppercase;
  letter-spacing: 0.11em;
  font-weight: 600;
}
input, textarea, select {
  background: var(--surface-3);
  color: var(--text);
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  padding: 9px 11px;
  font: inherit;
  font-size: 14px;
  width: 100%;
  min-width: 0;
  text-transform: none;
  letter-spacing: normal;
}
input::placeholder, textarea::placeholder { color: #6b7789; }
input:hover, textarea:hover, select:hover { border-color: var(--line-strong); }
input:focus-visible, textarea:focus-visible, select:focus-visible, button:focus-visible, [tabindex]:focus-visible {
  outline: 2px solid var(--primary);
  outline-offset: 2px;
  border-color: var(--primary);
}
textarea { resize: vertical; }
/* Versions, key ids, and signatures are read character by character. */
#d-version, #u-key, #u-signature, #p-confirm-version, #d-build { font-family: var(--mono); }

button {
  border-radius: var(--radius-sm);
  border: 1px solid var(--line-strong);
  padding: 9px 16px;
  font: inherit;
  font-size: 13px;
  font-weight: 700;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  cursor: pointer;
  background: var(--surface-3);
  color: var(--text);
  transition: background-color 120ms ease, border-color 120ms ease, color 120ms ease;
}
button:hover:not([disabled]) { border-color: var(--primary-dim); color: var(--primary); }
button.primary {
  background: var(--primary);
  border-color: var(--primary);
  color: var(--primary-ink);
}
button.primary:hover:not([disabled]) { background: #3dfce9; border-color: #3dfce9; color: var(--primary-ink); }
button.danger { background: transparent; border-color: var(--danger); color: var(--danger); }
button.danger:hover:not([disabled]) { background: var(--danger-wash); border-color: var(--danger); color: var(--danger); }
button.ghost { background: transparent; border-color: var(--line); color: var(--muted); }
button[disabled] { opacity: 0.4; cursor: not-allowed; }

/* --- tabs ----------------------------------------------------------------- */

.tabs {
  display: flex;
  gap: 4px;
  flex-wrap: wrap;
  padding: 4px;
  background: #0c1017a0;
  border: 1px solid var(--line);
  border-radius: var(--radius);
}
.tab {
  background: transparent;
  border-color: transparent;
  color: var(--muted);
  padding: 8px 15px;
  border-radius: var(--radius-sm);
}
.tab:hover:not([disabled]) { color: var(--text); background: var(--surface-2); border-color: transparent; }
.tab[aria-selected="true"] {
  color: var(--primary-ink);
  background: var(--primary);
  border-color: var(--primary);
}
.tab[aria-selected="true"]:hover { color: var(--primary-ink); }

/* --- layout --------------------------------------------------------------- */

.split { display: grid; grid-template-columns: minmax(300px, 390px) minmax(0, 1fr); gap: 16px; align-items: start; }
.view > .split > .card { min-height: 220px; }

/* --- the logged-out page --------------------------------------------------
 *
 * Signed out there is exactly one thing to do, so the card is the page: it sits
 * in the middle of the viewport rather than tucked into the top-left corner of
 * a layout built for dense tables. `data-auth` is in the served markup and is
 * flipped by app.js only after the server has authorized the operator, so a
 * console whose script never ran stays centred and stays empty.
 *
 * Centring is done with alignment only. Nothing here touches `display`, because
 * a `display` rule is the one thing that could fight the `[hidden]` boundary
 * above.
 */
body[data-auth="signed-out"] main {
  min-height: calc(100vh - 108px);
  justify-content: center;
  align-items: center;
}
body[data-auth="signed-out"] .signin { width: min(560px, 100%); }
body[data-auth="signed-out"] .signin h1 { font-size: 21px; }

.signin { max-width: 660px; margin-inline: auto; }
.signin-form { display: grid; gap: 12px; margin: 14px 0 6px; max-width: 380px; }
.signin-form button { justify-self: start; }

/* --- internal tester password ----------------------------------------------
 *
 * The same grid as the sign-in form, and deliberately no styling of its own
 * beyond that. This card must look like every other credential surface in the
 * console rather than like something special: nothing here is a reveal control,
 * a strength meter, a "show password" toggle, or a masked preview, because none
 * of those exists — the server cannot tell this screen what the password is.
 */
.tester-form { display: grid; gap: 12px; margin: 14px 0 6px; max-width: 460px; }
.tester-form .search-row { margin-top: 2px; }

/* --- provider sign-in ------------------------------------------------------
 *
 * "Continue with Google" is a plain ONYX button carrying the app's primary
 * cyan. There is no Google wordmark, logo, or brand asset: the console ships no
 * image of any kind, an external one would be blocked by `default-src 'none'`,
 * and a redrawn one would be a trademark this repository has no licence to
 * reproduce. The text label is also the accessible name, so it works the same
 * way for a screen reader and for a keyboard.
 */
.signin-google { display: grid; gap: 8px; margin: 16px 0 4px; }
.signin-google button.provider { justify-self: start; padding: 11px 20px; font-size: 13.5px; }
.signin-google p { margin: 0; }

/* A labelled rule between the two ways in, so neither reads as the only one. */
.signin-or {
  display: flex;
  align-items: center;
  gap: 12px;
  margin: 18px 0 2px;
  color: var(--muted);
  font-size: 10.5px;
  font-weight: 700;
  letter-spacing: 0.16em;
  text-transform: uppercase;
}
.signin-or::before, .signin-or::after { content: ""; flex: 1 1 auto; height: 1px; background: var(--line); }

/* The popup's landing page: one card, no chrome, gone in a moment. */
.callback { margin-top: 12vh; }

.search-row { display: flex; gap: 8px; align-items: center; }
.search-row input { flex: 1 1 auto; }
.search-row button { flex: 0 0 auto; }

/* --- result lists --------------------------------------------------------- */

.customer-list { list-style: none; margin: 14px 0 0; padding: 0; display: flex; flex-direction: column; gap: 12px; }
.customer-group {
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  background: #0c1118;
  overflow: hidden;
}
/*
 * The customer group heading. Since the 2026-08-25 customer/staff separation the
 * Vehicle Dashboard is the customer-service surface, so the person leads and the
 * salted reference is the auxiliary line beneath — the reverse of what it was.
 */
.customer-head {
  display: grid;
  grid-template-columns: 1fr auto;
  gap: 4px 10px;
  align-items: center;
  padding: 10px 12px;
  border-bottom: 1px solid var(--line);
  background: linear-gradient(90deg, var(--primary-wash), var(--surface-2) 48%);
}
.customer-name { font-weight: 700; font-size: 13px; overflow-wrap: anywhere; }
.customer-contact { grid-column: 1 / -1; display: flex; flex-wrap: wrap; gap: 4px 12px; }
.customer-contact-value { font-size: 11px; color: var(--text); overflow-wrap: anywhere; }
.customer-contact-value.muted { color: var(--muted); }
.customer-reference { font-family: var(--mono); font-weight: 600; font-size: 11px; color: var(--muted); overflow-wrap: anywhere; }
.customer-count { color: var(--muted); font-size: 10.5px; text-transform: uppercase; letter-spacing: 0.1em; white-space: nowrap; }
.customer-vehicles { list-style: none; margin: 0; padding: 8px; display: flex; flex-direction: column; gap: 7px; }

.account-vehicle-group { border: 1px solid var(--line); border-radius: var(--radius-sm); background: #0c1118; overflow: hidden; margin: 10px 0; }
.account-vehicle-toggle { width: 100%; display: grid; grid-template-columns: minmax(0, 1fr) auto; gap: 4px 12px; padding: 11px 13px; text-align: left; background: linear-gradient(90deg, var(--primary-wash), var(--surface-2) 48%); border: 0; border-radius: 0; text-transform: none; letter-spacing: normal; }
.account-vehicle-toggle:hover:not([disabled]) { background: var(--surface-2); color: var(--text); }
.account-primary { font-weight: 750; font-size: 14px; overflow-wrap: anywhere; }
.account-secondary { grid-column: 1 / -1; display: flex; flex-wrap: wrap; gap: 5px 12px; color: var(--muted); font-size: 11px; }
.account-category { border: 1px solid var(--primary-dim); border-radius: var(--radius-pill); color: var(--primary); padding: 1px 7px; font-weight: 700; }
.account-vehicle-list { border-top: 1px solid var(--line); padding: 8px; display: grid; gap: 7px; }
.account-vehicle-card { background: var(--surface); border: 1px solid var(--line); border-radius: var(--radius-sm); }
.account-vehicle-card[aria-current="true"] { border-color: var(--primary); box-shadow: 0 0 0 1px var(--primary-dim); }
.account-vehicle-summary { cursor: pointer; padding: 10px 12px; font-weight: 700; font-family: var(--mono); }
.account-vehicle-card > .facts, .account-vehicle-card > button { margin: 0 10px 10px; }
.compact-facts { grid-template-columns: repeat(auto-fit, minmax(180px, 1fr)); }

.vehicle-list { list-style: none; margin: 12px 0 0; padding: 0; display: flex; flex-direction: column; gap: 8px; }
.vehicle-list button, .customer-vehicle {
  width: 100%;
  text-align: left;
  display: grid;
  gap: 4px;
  background: var(--surface);
  border-color: var(--line);
  font-weight: 400;
  font-size: 13px;
  text-transform: none;
  letter-spacing: normal;
  padding: 11px 13px;
  border-left: 3px solid transparent;
}
.vehicle-list button:hover:not([disabled]), .customer-vehicle:hover:not([disabled]) { background: var(--surface-2); color: var(--text); border-color: var(--line); border-left-color: var(--primary-dim); }
.vehicle-list button[aria-current="true"], .customer-vehicle[aria-current="true"] {
  border-color: var(--line);
  border-left-color: var(--primary);
  background: linear-gradient(90deg, var(--primary-wash), var(--surface-2) 40%);
}
.vehicle-head { display: flex; gap: 8px; align-items: center; justify-content: space-between; flex-wrap: wrap; }
.vehicle-serial { font-weight: 700; letter-spacing: 0.05em; font-family: var(--mono); font-size: 13px; }
.vehicle-meta { color: var(--muted); font-size: 12px; }

/* --- device state pills ---------------------------------------------------
 *
 * One rule per value of `supportStatuses` in `src/diagnostics/model.ts`, and a
 * test asserts that mapping is complete: a status with no rule here would render
 * as an unstyled pill and read as "fine".
 *
 * `attention_required` is AMBER, not red. Red reads as "stop riding", and this
 * console is forbidden from implying a severity: ONYX publishes no verified
 * meaning for a controller WarnCode or ErrCode, so all the server actually knows
 * is that a nonzero value was read and a human should look. Amber says that.
 * Red would be this stylesheet inventing a diagnosis.
 */
.status {
  display: inline-block;
  border-radius: var(--radius-pill);
  padding: 3px 10px;
  font-size: 10.5px;
  font-weight: 800;
  letter-spacing: 0.11em;
  text-transform: uppercase;
  border: 1px solid currentColor;
  white-space: nowrap;
  color: var(--unknown);
}
.status-clear { color: var(--ok); }
.status-no_report { color: var(--unknown); }
.status-codes_not_reported { color: var(--unknown); }
.status-attention_required { color: var(--warn); background: var(--warn-wash); }

/* --- detail panes --------------------------------------------------------- */

.detail-head { display: flex; gap: 12px; align-items: center; flex-wrap: wrap; }
.facts { display: grid; grid-template-columns: repeat(auto-fit, minmax(196px, 1fr)); gap: 10px; margin: 16px 0 0; }
.facts div {
  background: var(--surface-3);
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  padding: 9px 12px;
}
.facts dt {
  color: var(--muted);
  font-size: 10.5px;
  text-transform: uppercase;
  letter-spacing: 0.12em;
  font-weight: 700;
}
.facts dd { margin: 4px 0 0; font-weight: 600; overflow-wrap: anywhere; font-family: var(--mono); font-size: 13px; }

/* --- tables --------------------------------------------------------------- */

.table-wrap {
  overflow-x: auto;
  -webkit-overflow-scrolling: touch;
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
}
table { border-collapse: collapse; width: 100%; font-size: 13px; }
.community-review { min-width: 1180px; }
.community-review details { margin-top: 7px; color: var(--muted); }
.community-review details p { white-space: pre-wrap; max-width: 38rem; color: var(--text-dim); }
.review-actions { display: flex; flex-direction: column; gap: 7px; min-width: 150px; }
.review-actions button { width: 100%; }
th, td { text-align: left; padding: 8px 11px; border-bottom: 1px solid var(--line); white-space: nowrap; }
tbody tr:last-child td { border-bottom: none; }
tbody tr:hover { background: #ffffff06; }
th {
  color: var(--muted);
  font-weight: 700;
  font-size: 10.5px;
  text-transform: uppercase;
  letter-spacing: 0.12em;
  background: #0c1017;
  position: sticky;
  top: 0;
}
td { font-variant-numeric: tabular-nums; }
td.wrap { white-space: normal; min-width: 220px; }
/* A nonzero controller code is amber for the same reason the pill is. */
.code-nonzero { color: var(--warn); font-weight: 700; font-family: var(--mono); }
.code-zero { color: var(--ok); font-weight: 700; font-family: var(--mono); }

/* --- release workflow ----------------------------------------------------- */

.grid-form { display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: 12px; align-items: end; }
.grid-form .wide { grid-column: 1 / -1; }
.grid-form .checkbox { display: flex; align-items: center; gap: 8px; color: var(--text-dim); text-transform: none; letter-spacing: normal; font-size: 13px; font-weight: 500; }
.grid-form .checkbox input { width: auto; accent-color: var(--primary); }
.publish-row { display: flex; gap: 8px; flex-wrap: wrap; align-items: end; margin-top: 14px; }
.publish-row select, .publish-row input { flex: 1 1 180px; }
.publish-row label { display: flex; flex-direction: column; gap: 6px; flex: 1 1 220px; }

/*
 * Draft, published, and revoked must never have to be told apart by reading
 * carefully. One rule per state, and a test asserts the mapping is complete.
 */
.release-state {
  display: inline-block;
  border-radius: var(--radius-pill);
  padding: 3px 10px;
  font-size: 10.5px;
  font-weight: 800;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  border: 1px solid currentColor;
  white-space: nowrap;
  color: var(--unknown);
}
.release-draft { color: var(--unknown); }
/* Cyan, the "live" colour: published is the state customers actually receive. */
.release-published { color: var(--primary); background: var(--primary-wash); }
.release-revoked { color: var(--danger); }

/*
 * Account types.
 *
 * One rule per type, on the same terms as the release states above: the pill
 * class is built by concatenation, so a type with no rule renders unstyled, and
 * unstyled reads as "ordinary" — exactly backwards for an elevated account. The
 * base rule carries the legible default so an unmapped value is still readable.
 *
 * Amber, not red, for the two elevated types. Amber is this console's "an
 * operator should notice this" colour; red asserts a fault, and an admin
 * account is not a fault.
 */
.account-type {
  display: inline-block;
  border-radius: var(--radius-pill);
  padding: 3px 10px;
  font-size: 10.5px;
  font-weight: 800;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  border: 1px solid currentColor;
  white-space: nowrap;
  color: var(--unknown);
}
.account-customer { color: var(--unknown); }
.account-engineer { color: var(--primary); background: var(--primary-wash); }
.account-admin { color: var(--warn); }
.account-super_admin { color: var(--warn); background: var(--warn-wash); }

.account-subject { font-family: var(--mono); font-size: 12px; word-break: break-all; }
.account-audience { color: var(--text-dim); font-size: 12px; }
.account-type-select { margin-right: 8px; }

/**
 * The live customer directory.
 *
 * Eight columns of mostly short values, so the table keeps the admin density
 * rule: type stays small and rows stay tight rather than being spaced out for
 * atmosphere at the cost of how many customers fit on the screen. The provider
 * detail is rendered in the same dim tone as every other read-only value —
 * nothing here is coloured to suggest a status, because a display name and an
 * email address carry no state.
 */
.accounts.directory td { font-size: 12px; }
.accounts.directory .directory-unset { color: var(--text-dim); font-style: italic; }

/*
 * Keep the two action columns physically separate on narrow screens.
 *
 * A select is globally width:100%. Previously it and the Change button lived
 * directly in one nowrap table cell, so the button overflowed into the next
 * cell and painted on top of Show bikes. The table may scroll horizontally on
 * a phone, but controls must never overlap or double-render.
 */
.accounts.directory.hierarchy { min-width: 980px; }
.accounts.directory.hierarchy .account-action-cell {
  min-width: 245px;
  white-space: normal;
}
.account-action-controls {
  display: grid;
  grid-template-columns: minmax(120px, 1fr) auto;
  gap: 8px;
  align-items: center;
}
.account-action-controls .account-type-select { width: 100%; margin: 0; }
.account-action-controls button { min-width: 96px; white-space: nowrap; }
.accounts.directory.hierarchy .associated-bikes-action {
  min-width: 145px;
  white-space: normal;
}
.associated-bikes-action button {
  display: block;
  width: 100%;
  min-width: 120px;
  white-space: nowrap;
}

/**
 * The customer-first hierarchy.
 *
 * The compact table carries six columns instead of eight, because the Firebase
 * user id, the provider registration time, and this server's first-seen time all
 * moved into the expansion an operator deliberately opens. That is a privacy
 * decision rendered as a layout one: a column of a thousand user ids is a screen
 * somebody can photograph, and a single one under an open row is not.
 *
 * The expanded area is a full-width row rather than a floating panel so it
 * cannot be left open over another customer's row, and it is visually recessed
 * so it reads as belonging to the row above it.
 */
.directory-expansion > td {
  background: var(--surface-2);
  border-left: 2px solid var(--primary);
  padding: 14px 16px;
}
.directory-expansion h3 {
  margin: 0 0 6px;
  color: var(--warn);
  letter-spacing: 0.06em;
  text-transform: uppercase;
}
/**
 * Amber, never red, and for the same reason it is amber everywhere else in this
 * console: "unverified vehicle identity" is something an operator must notice,
 * not a fault ONYX has published.
 */
.vehicle-cards {
  display: flex;
  flex-wrap: wrap;
  gap: 10px;
  margin: 8px 0;
}
.vehicle-card {
  flex: 1 1 260px;
  min-width: 240px;
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  padding: 10px 12px;
}
.vehicle-card p { margin: 0 0 6px; }
.vehicle-detail {
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  padding: 10px 12px;
  margin: 8px 0;
}
.vehicle-detail h4 {
  margin: 0 0 6px;
  color: var(--text-dim);
  letter-spacing: 0.06em;
  text-transform: uppercase;
}
.vehicle-detail p { margin: 0 0 6px; }
/**
 * The one line telling an operator whether the screen is live.
 *
 * It sits under the table on purpose: "refreshing every 30 seconds", "paused",
 * and "stopped" are three different facts about what they are looking at, and
 * an operator quoting a stale row to a customer is exactly what this line is
 * for. It is never coloured as an error — a paused refresh is a normal state.
 */
#directory-refresh-state { font-family: var(--mono); font-size: 11px; }

/* The audience picker reads as one decision rather than four stray checkboxes. */
.audience {
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  padding: 10px 12px;
  margin: 0;
}
.audience legend { color: var(--text-dim); font-size: 11px; font-weight: 700; letter-spacing: 0.08em; text-transform: uppercase; padding: 0 4px; }
.audience .checkbox { display: flex; align-items: center; gap: 8px; color: var(--text-dim); text-transform: none; letter-spacing: normal; font-size: 13px; font-weight: 500; }
.audience .checkbox input { width: auto; accent-color: var(--primary); }

.confirm-box {
  margin-top: 12px;
  padding: 13px 15px;
  border: 1px solid var(--warn);
  border-left-width: 3px;
  border-radius: var(--radius-sm);
  background: var(--warn-wash);
  display: grid;
  gap: 10px;
}
.confirm-box label { color: var(--text-dim); font-size: 11px; }
.confirm-box .checkbox { display: flex; flex-direction: row; align-items: center; gap: 8px; text-transform: none; letter-spacing: normal; font-size: 13px; font-weight: 500; }
.confirm-box .checkbox input { width: auto; accent-color: var(--warn); }

/* --- notices and banners -------------------------------------------------- */

.notice {
  position: sticky;
  bottom: 12px;
  margin: 0;
  padding: 12px 15px;
  border-radius: var(--radius-sm);
  border: 1px solid var(--line-strong);
  background: var(--surface-3);
  box-shadow: 0 10px 26px #00000055;
  font-size: 13.5px;
}
.notice[data-tone="error"] { border-color: var(--danger); background: var(--danger-wash); }
.notice[data-tone="ok"] { border-color: var(--primary-dim); background: var(--primary-wash); }

/*
 * Standing qualifier above a view whose data must not be read as a vehicle
 * record. It is deliberately the first thing in the view and is never dismissed:
 * an agent reading firmware versions and error codes will otherwise assume the
 * vehicle behind them is identified, and it is not.
 */
.banner {
  margin: 0 0 14px;
  padding: 12px 15px;
  border-radius: var(--radius-sm);
  border: 1px solid var(--warn);
  border-left-width: 3px;
  background: var(--warn-wash);
  color: var(--text-dim);
  font-size: 12.5px;
  line-height: 1.55;
}
.vehicle-meta.unverified {
  color: var(--warn);
  text-transform: uppercase;
  letter-spacing: 0.11em;
  font-size: 10.5px;
  font-weight: 700;
}

.sr-only { position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0; }

/* --- accessibility -------------------------------------------------------- */

/*
 * The palette above is chosen for contrast on the dark base, but a forced-colours
 * or high-contrast user has overridden it for a reason: let them, and keep the
 * borders that carry the state distinctions.
 */
@media (forced-colors: active) {
  .status, .release-state, .tab[aria-selected="true"], button { border: 1px solid; }
  .card::before, .top::after, .loading::after { display: none; }
}

@media (max-width: 900px) {
  .split { grid-template-columns: 1fr; }
  main { padding: 14px; }
  th, td { padding: 7px 9px; }
  .brand-mark { font-size: 17px; }
}
