/* Rules the console never wrote.
 *
 * Its pages only ever render empty states — no key was ever listed, no key was
 * ever revealed — so there is nothing to copy for either. These are written in
 * the console's own idiom and use only its tokens, and they live apart from
 * app.css so that file stays a verbatim copy that can be regenerated.
 */

/* ── a populated key table ───────────────────────────────────────────────── */

.keys-row {
  min-width: 760px;
  min-height: 66px;
  padding: 0 23px;
  display: grid;
  grid-template-columns: 1.1fr .8fr 1.1fr .6fr 35px;
  align-items: center;
  border-bottom: 1px solid var(--app-border);
  font-size: 11px;
}
.keys-row:last-child { border-bottom: 0; }
.keys-row code {
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  font-size: 11px;
  color: var(--app-text);
}
.keys-row .keys-when { color: var(--app-muted); font-size: 10px; }
/* Digits in a column only line up if they are tabular. */
.keys-row .keys-when,
.keys-row code { font-variant-numeric: tabular-nums; }
.keys-row .keys-revoke {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 30px;
  height: 30px;
  border-radius: 9px;
  border: 1px solid var(--app-border);
  color: var(--app-muted);
}
.keys-row .keys-revoke:hover { color: #e2807d; border-color: rgba(226, 128, 125, .35); }
.keys-table-scroll { overflow-x: auto; }

/* ── the key, shown once ─────────────────────────────────────────────────── */

.key-reveal {
  margin: 0 0 22px;
  padding: 22px 24px;
  border: 1px solid rgba(57, 198, 128, .28);
  border-radius: 18px;
  background: rgba(38, 166, 105, .06);
  display: grid;
  gap: 14px;
}
.key-reveal h2 { font-size: 15px; font-weight: 700; }
.key-reveal p { color: var(--app-muted); font-size: 11px; max-width: 62ch; }
.key-reveal-value {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 13px 15px;
  border: 1px solid var(--app-border);
  border-radius: 12px;
  background: var(--app-surface-2);
}
.key-reveal-value code {
  flex: 1;
  overflow-x: auto;
  white-space: nowrap;
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  font-size: 12px;
  letter-spacing: .01em;
}
.key-reveal-copy {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  padding: 8px 13px;
  border: 1px solid var(--app-border);
  border-radius: 10px;
  font-size: 11px;
  white-space: nowrap;
}
.key-reveal-copy[data-copied="1"] { color: #3ed38b; border-color: rgba(57, 198, 128, .4); }

/* ── inline form controls that sit inside the console's own toolbars ─────── */

.inline-form { display: contents; }

/* ── populated catalog and register rows ─────────────────────────────────── */
/*
 * The console lists neither: its catalog and register only ever show the state
 * before a gateway is connected. Grid columns are copied from the matching
 * table heads it did write, so a row lines up with its header.
 */

.model-row,
.usage-row {
  min-width: 700px;
  min-height: 58px;
  padding: 0 23px;
  display: grid;
  align-items: center;
  border-bottom: 1px solid var(--app-border);
  font-size: 11px;
}
.model-row { grid-template-columns: 1.5fr .9fr .9fr .7fr .8fr 35px; }
.usage-row { grid-template-columns: 1fr 1.3fr .6fr .6fr .8fr .6fr; }
.model-row:last-child,
.usage-row:last-child { border-bottom: 0; }

.model-row code,
.usage-row code {
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  font-size: 11px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  padding-right: 12px;
}
.model-kind { color: var(--app-subtle); font-size: 10px; }

/* Figures only compare down a column if they are tabular and right-aligned. */
.model-price,
.usage-num,
.usage-when {
  font-variant-numeric: tabular-nums;
}
.model-price,
.usage-num { text-align: right; padding-right: 14px; }
.usage-when { color: var(--app-muted); font-size: 10px; }

/* An estimate has to look like one, or it reads as a measurement. */
.usage-num b {
  margin-left: 6px;
  padding: 2px 6px;
  border: 1px solid rgba(231, 173, 70, .28);
  border-radius: 20px;
  color: #bca16e;
  font-size: 8px;
  font-weight: 700;
  text-transform: uppercase;
}

/* Filters and paging are links here rather than React state, so they need the
 * button styling the console gave to its <button> elements. */
.catalog-tabs a,
.segmented-control a,
.period-control a,
.table-footer a {
  display: inline-flex;
  align-items: center;
  gap: 6px;
}
.catalog-controls form { display: contents; }

/* ── administrative rows ─────────────────────────────────────────────────── */
/*
 * The console's admin lists nothing either: every registry it drew is an empty
 * state waiting for a payment system. Columns match the .admin-table-head
 * variants it did write.
 */

.admin-row {
  min-width: 760px;
  min-height: 54px;
  margin: 0 -24px;
  padding: 0 24px;
  display: grid;
  grid-template-columns: 1fr 1.2fr .8fr .9fr .7fr;
  align-items: center;
  border-bottom: 1px solid var(--admin-border);
  font-size: 11px;
}
.admin-row:last-of-type { border-bottom: 0; }
.admin-row small { display: block; color: var(--admin-subtle); font-size: 9px; margin-top: 3px; }

.admin-table-head.registry-customers,
.admin-row.registry-customers { grid-template-columns: 1.6fr .8fr .9fr .9fr .7fr .5fr; }

.admin-value { font-variant-numeric: tabular-nums; font-weight: 700; }
.admin-hint { color: var(--admin-subtle); font-size: 10px; }
.admin-num { font-variant-numeric: tabular-nums; text-align: right; padding-right: 16px; }

/* The tariff cards became forms; the console drew them as static fields. */
.tariff-editor form { display: contents; }
.tariff-fields input[readonly] { opacity: .65; cursor: default; }

/* ── the pricing screen ──────────────────────────────────────────────────── */
/*
 * Not in the console at all: its admin covers the payment circuit, and the
 * comparison against a vendor's own price is an operations screen. Built from
 * the console's own admin tokens.
 */

.admin-table-head.pricing,
.admin-row.pricing { grid-template-columns: 1.7fr .8fr .8fr .8fr .9fr .6fr 1fr; }
.admin-row.pricing code { font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; font-size: 11px; }

/* A row that crosses the vendor's price has to be visible without reading. */
.admin-row.pricing.over { background: rgba(197, 60, 55, .07); }
.admin-row.pricing.over [data-our] { color: #e2807d; font-weight: 700; }

.stale-ref {
  margin-left: 8px; padding: 2px 7px;
  border: 1px solid rgba(231, 173, 70, .3); border-radius: 20px;
  color: #bca16e; font-size: 8px; font-weight: 700; text-transform: uppercase;
}

/* Same pill, without the warning colour: a model priced in dollars is a state
   to notice, not a problem to fix. */
.priced-ref {
  margin-left: 8px; padding: 2px 7px;
  border: 1px solid var(--admin-border); border-radius: 20px;
  color: var(--admin-muted); font-size: 8px; font-weight: 700; text-transform: uppercase;
}

.pricing-markup {
  margin: 18px 0 4px;
  display: flex; align-items: center; flex-wrap: wrap; gap: 14px;
  padding: 16px 18px;
  border: 1px solid var(--admin-border); border-radius: 14px;
  background: var(--admin-surface-2, var(--panel-2));
}
.pricing-markup label { font-size: 11px; color: var(--admin-subtle); }
.pricing-markup input[type="range"] { flex: 1; min-width: 180px; accent-color: var(--blue); }
.pricing-markup output {
  min-width: 3.5rem; text-align: right;
  font-variant-numeric: tabular-nums; font-size: 20px; font-weight: 700;
}
.pricing-hint { font-size: 10px; color: var(--admin-subtle); }

.pricing-tariff-switch { display: flex; gap: 8px; flex-wrap: wrap; }
.pricing-tariff-switch a {
  padding: 8px 14px;
  border: 1px solid var(--admin-border); border-radius: 20px;
  font-size: 11px; color: var(--admin-muted);
}
.pricing-tariff-switch a.active { color: var(--text); border-color: var(--line-bright); }

/* ── the administrative register and the tariff model editor ─────────────── */

.admin-table-head.ledger,
.admin-row.ledger { grid-template-columns: .8fr 1.3fr 1.3fr .6fr .6fr .8fr .8fr .5fr .6fr; }
.admin-table-head.models,
.admin-row.models { grid-template-columns: 1.8fr .5fr .9fr .9fr 1.1fr .7fr; }

.admin-when { color: var(--admin-subtle); font-size: 10px; font-variant-numeric: tabular-nums; }
.admin-row.ledger code,
.admin-row.models code {
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  font-size: 10px;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap; display: block;
  padding-right: 10px;
}
/* A model already in the tariff should be findable in a long catalog at a
   glance, rather than by reading every badge. */
.admin-row.models.granted { background: rgba(57, 198, 128, .045); }

.model-actions { display: flex; gap: 6px; justify-content: flex-end; padding-right: 6px; }
.model-actions button {
  display: inline-flex; align-items: center; justify-content: center;
  width: 28px; height: 28px;
  border: 1px solid var(--admin-border); border-radius: 8px;
  color: var(--admin-muted);
}
.model-actions button:hover { color: var(--text); border-color: var(--line-bright); }

/* ── the credentials screen ──────────────────────────────────────────────── */

.secret-row {
  display: grid;
  grid-template-columns: 1.3fr .9fr 1.2fr auto;
  gap: 14px;
  align-items: center;
  padding: 16px 0;
  border-bottom: 1px solid var(--admin-border);
}
.secret-row:last-child { border-bottom: 0; }
.secret-label strong { display: block; font-size: 12px; }
.secret-label small { display: block; margin-top: 4px; color: var(--admin-subtle); font-size: 10px; }
.secret-state { display: flex; align-items: center; flex-wrap: wrap; gap: 8px; }
.secret-state code {
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  font-size: 11px;
}
@media (max-width: 900px) {
  .secret-row { grid-template-columns: 1fr; }
}

/* The restart control sits inside a warning, not on its own panel: it is a
   consequence of the message above it, and separating them would let somebody
   read one without the other. */
.admin-inline-form {
  display: flex;
  align-items: center;
  gap: 0.75rem;
  flex-wrap: wrap;
  margin-top: 0.75rem;
}
.admin-inline-form small {
  color: var(--muted-foreground);
  font-size: 0.75rem;
  max-width: 34rem;
}

/* The documentation page. Examples are meant to be READ and copied, so they
   get the width and the leading of prose, not the cramped mono of a log. */
.docs-examples {
  display: grid;
  gap: 0.75rem;
}
.docs-example {
  border: 1px solid var(--ui-stroke-secondary);
  border-radius: 10px;
  overflow: hidden;
  background: var(--card);
}
.docs-example header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
  padding: 0.6rem 0.9rem;
  border-bottom: 1px solid var(--ui-stroke-secondary);
}
.docs-lang {
  font-size: 0.7rem;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--muted-foreground);
}
.docs-example pre {
  margin: 0;
  padding: 0.9rem;
  /* Horizontal scroll on the BLOCK, never on the page: a long curl line must
     not make the whole cabinet scroll sideways. */
  overflow-x: auto;
  font-size: 0.8rem;
  line-height: 1.6;
}
.docs-example code {
  font-family: var(--font-mono, ui-monospace, SFMono-Regular, Menlo, monospace);
  white-space: pre;
}

.docs-notes {
  display: grid;
  gap: 0.75rem;
  grid-template-columns: repeat(auto-fit, minmax(19rem, 1fr));
}
.docs-notes article {
  display: flex;
  gap: 0.75rem;
  align-items: flex-start;
  padding: 0.9rem;
  border: 1px solid var(--ui-stroke-secondary);
  border-radius: 10px;
  background: var(--card);
}
.docs-notes strong {
  display: block;
  margin-bottom: 0.2rem;
  font-size: 0.85rem;
}
.docs-notes p {
  margin: 0;
  font-size: 0.78rem;
  line-height: 1.55;
  color: var(--muted-foreground);
}

/* The security explainer reuses the auth card, which is narrower than this
   page needs — it is read, not filled in. */
.security-card {
  max-width: 34rem;
}
.security-item {
  padding: 0.65rem 0;
  border-bottom: 1px solid var(--ui-stroke-secondary);
}
.security-item:last-of-type {
  border-bottom: none;
}
.security-item strong {
  display: flex;
  align-items: center;
  gap: 0.4rem;
  font-size: 0.85rem;
  margin-bottom: 0.25rem;
}
.security-item p {
  margin: 0;
  font-size: 0.78rem;
  line-height: 1.55;
  color: var(--muted-foreground);
}
.security-separator {
  display: flex;
  align-items: center;
  gap: 0.6rem;
  margin: 0.9rem 0 0.3rem;
  font-size: 0.7rem;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--muted-foreground);
}
.security-separator::before,
.security-separator::after {
  content: '';
  flex: 1;
  height: 1px;
  background: var(--ui-stroke-secondary);
}

/* ── two selectors that never matched the markup ─────────────────────────── */
/*
 * Both are in app.css, which is a verbatim copy of the console's stylesheet
 * and is regenerated — so the fix belongs here rather than there.
 *
 * app.css styles `.admin-form-panel > label`. Every form on these pages wraps
 * its fields in `<form class="admin-form-grid">`, which makes the labels
 * GRANDCHILDREN, so the rule matched nothing: the caption rendered inline
 * against its own input and read as one run-on string — "Почта клиентаyou@
 * example.com". Selects escaped it because their rule has no child combinator,
 * which is why half of each form looked right and the other half did not.
 */
.admin-form-panel .admin-form-grid label,
.admin-form-panel form > label {
  display: block;
  color: #929ca6;
  font-size: 11px;
}
.admin-form-panel .admin-form-grid input,
.admin-form-panel .admin-form-grid select,
.admin-form-panel form > label input {
  width: 100%;
  height: 44px;
  margin-top: 7px;
  padding: 0 12px;
  border: 1px solid var(--admin-border);
  border-radius: 8px;
  background: #0c0f12;
  color: #cdd3d9;
  font-size: 11px;
}
.admin-form-panel .admin-form-grid input::placeholder { color: #4e565f; }
.admin-form-panel .admin-form-grid input:focus,
.admin-form-panel .admin-form-grid select:focus {
  outline: none;
  border-color: rgba(227, 164, 59, .45);
}
/* The button sits in the grid beside a labelled field, so it has to clear the
   caption the field above it is wearing or the two rows do not line up. */
.admin-form-panel .admin-form-grid > button { align-self: end; height: 44px; }

/*
 * The tab strips are a narrower miss than that, and worth stating precisely.
 * `.segmented-control a` above DOES match — it was written to give the links
 * `display: inline-flex` and a gap between an icon and its text. What it never
 * carried is the box: `.segmented-control button` in app.css has the height,
 * the padding and the active state, and every tab strip here is built from
 * links rather than buttons, because a tab is a URL you can bookmark and
 * reload. So the links laid out correctly and touched, with nothing between
 * one word and the next: "Все запросыУспешныеС ошибкойОценочные".
 */
.segmented-control a {
  display: inline-flex;
  align-items: center;
  height: 30px;
  padding: 0 11px;
  border: 1px solid transparent;
  border-radius: 8px;
  color: var(--app-muted, var(--admin-muted));
  font-size: 11px;
  white-space: nowrap;
}
.segmented-control a:hover { color: var(--text); }
.segmented-control a.active {
  border-color: rgba(48, 161, 244, .22);
  background: rgba(25, 139, 224, .1);
  color: #4dbbfa;
}
.segmented-control { gap: 2px; flex-wrap: wrap; }

/* ── stopping and removing a customer ────────────────────────────────────── */

.admin-table-head.registry-customers,
.admin-row.registry-customers { grid-template-columns: 1.5fr .7fr .8fr .8fr .6fr .4fr .9fr; }

.customer-actions { display: flex; gap: 6px; justify-content: flex-end; padding-right: 6px; }
.customer-actions form { display: inline; }
.customer-actions button {
  display: inline-flex; align-items: center; justify-content: center;
  width: 28px; height: 28px;
  border: 1px solid var(--admin-border); border-radius: 8px;
  background: transparent; color: var(--admin-muted);
}
.customer-actions button:hover { color: var(--text); border-color: var(--line-bright); }
/* Deletion is the one irreversible control on the page, so it is the one that
   colours on hover. Not by default: a row of red buttons stops being a
   warning and becomes decoration. */
.customer-actions button.destructive:hover {
  color: #e2807d; border-color: rgba(226, 106, 106, .4);
}
.admin-row.blocked { background: rgba(226, 106, 106, .05); }
.admin-row.blocked > span:first-child { color: #b98d8d; }

/* ── what a route actually did ───────────────────────────────────────────── */

.admin-table-head.models,
.admin-row.models { grid-template-columns: 1.6fr .4fr .75fr .75fr .95fr .8fr .7fr .55fr; }

.route-state { display: flex; flex-direction: column; gap: 3px; font-size: 9px; }
.route-state small { color: var(--admin-subtle); margin: 0; }
.route-state .bad { color: #e2807d; }

/* The answering model, under the requested one. Dim, because it is the
   second-order fact: the id above it is what was asked for and what was
   priced. */
.answered-by {
  display: block;
  margin-top: 3px;
  color: var(--admin-subtle);
  font-size: 9px;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.answered-by::before { content: '↳ '; }

/* ── the economics table ─────────────────────────────────────────────────── */

.admin-table-head.economics,
.admin-row.economics { grid-template-columns: 1.6fr .5fr .8fr .8fr .7fr .7fr .7fr .5fr; }
.admin-row.economics code {
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  font-size: 10px;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap; display: block;
  padding-right: 10px;
}
.admin-row.economics.muted { opacity: .55; }
.admin-num.loss { color: #e2807d; }
.admin-num.gain { color: #6fc79a; }

/* ── the token glossary ──────────────────────────────────────────────────── */
/*
 * Three different things are called a token on these screens, and the numbers
 * only make sense once you know which is which. The panel is laid out as
 * definitions rather than prose so it can be scanned mid-task.
 */
.token-glossary { display: grid; gap: 14px; margin-top: 4px; }
.token-glossary > div {
  display: grid;
  grid-template-columns: 150px minmax(0, 1fr);
  gap: 14px;
  padding-bottom: 12px;
  border-bottom: 1px solid var(--admin-border);
}
.token-glossary > div:last-child { border-bottom: 0; padding-bottom: 0; }
.token-glossary dt, .token-glossary strong {
  color: #e3c185; font-size: 11px; font-weight: 700;
}
.token-glossary p { margin: 0; color: var(--admin-muted); font-size: 11px; line-height: 1.6; }
.token-glossary code {
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  font-size: 10px; color: #cdd3d9;
}
@media (max-width: 720px) {
  .token-glossary > div { grid-template-columns: 1fr; gap: 5px; }
}

/* The audit table asks two of its columns not to wrap, and nothing was
   listening: a timestamp broken across two lines and an address broken after
   the second octet are both harder to read than the row is tall. */
.nowrap { white-space: nowrap; }

/* ── цена, посчитанная от вендора вниз ───────────────────────────────────── */

.admin-table-head.vendorprice,
.admin-row.vendorprice { grid-template-columns: 1.5fr 1.1fr 1.1fr .7fr 1.1fr .6fr .9fr; }
.admin-row.vendorprice code {
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  font-size: 10px;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap; display: block;
  padding-right: 10px;
}
/* The model whose implied package price is the lowest is the one everybody
   else's price has to respect. It has to be findable without reading the
   column. */
.admin-row.vendorprice.binding { background: rgba(227, 164, 59, .07); }
.admin-row.vendorprice.loss { background: rgba(226, 106, 106, .08); }

/* ── removing a tariff ───────────────────────────────────────────────────── */

.tariff-delete { margin-top: 10px; }
.tariff-delete button {
  width: 100%;
  display: inline-flex; align-items: center; justify-content: center; gap: 6px;
  height: 38px;
  border: 1px solid var(--admin-border); border-radius: 8px;
  background: transparent; color: var(--admin-muted);
  font-size: 11px;
}
.tariff-delete button:hover { color: #e2807d; border-color: rgba(226, 106, 106, .4); }

/* Said rather than hidden. A control that vanishes without explanation reads
   as a missing feature; this reads as a rule. */
.tariff-delete-blocked {
  margin: 10px 0 0;
  display: flex; align-items: center; gap: 6px;
  color: var(--admin-subtle); font-size: 10px; line-height: 1.5;
}

/* The wait column carries a number and a share under it; the number is the
   one read at a glance, so it is the one with weight. */
.route-state strong { font-size: 11px; font-variant-numeric: tabular-nums; }
.route-state strong.bad { color: #e2807d; }
.route-state small.bad { color: #b5726f; }

/* ── the documentation reference tables ──────────────────────────────────── */

.admin-table-head.docs-endpoints,
.admin-row.docs-endpoints { grid-template-columns: .5fr 1.6fr 2.4fr .7fr; }
.admin-table-head.docs-errors,
.admin-row.docs-errors { grid-template-columns: .4fr 1.2fr 1.4fr 2.4fr .6fr; }

.admin-row.docs-endpoints,
.admin-row.docs-errors {
  align-items: start;
  padding-top: 14px;
  padding-bottom: 14px;
  line-height: 1.55;
}
.admin-row.docs-endpoints code,
.admin-row.docs-errors code {
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  font-size: 10px;
  overflow-wrap: anywhere;
}
/* A closed route is still information, so it is dimmed rather than hidden —
   but the reason under it is the part worth reading, so that keeps its
   contrast. */
.admin-row.docs-endpoints.closed > span:nth-child(2) { opacity: .6; }
.admin-row.docs-endpoints small {
  display: block;
  margin-top: 5px;
  color: var(--admin-subtle);
  font-size: 10px;
  line-height: 1.6;
}
.docs-method,
.docs-status {
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  font-size: 10px;
  font-weight: 700;
  color: var(--admin-muted);
}
.admin-row.docs-errors em {
  font-style: normal;
  color: #d7b578;
}

/* An image model's price is per picture, and the row has to say so or the
   figure reads as a per-million rate — which is what it used to be, wrongly. */
.model-price small,
.admin-num small {
  display: block;
  margin-top: 3px;
  color: var(--admin-subtle, var(--muted-foreground));
  font-size: 9px;
  font-weight: 400;
  line-height: 1.45;
  text-align: right;
}
.model-price small { text-align: left; }

/* ── bringing a deleted customer back ────────────────────────────────────────
   Same registry shape as the customer list, one column narrower: there is no
   tariff to show on a row whose account is closed. The last column holds a form
   rather than a figure, so it is the widest. */
.admin-table-head.registry-deleted,
.admin-row.registry-deleted { grid-template-columns: .7fr 1.3fr .6fr .9fr 2fr; }

/* The one-time-code key, shown once and read off the screen by hand. Wide
   letter spacing because the difference between an O and a 0 decides whether
   the panel opens tomorrow. */
.totp-secret {
  margin-top: 8px; padding: 14px 16px;
  border: 1px solid var(--admin-border); border-radius: 10px;
  background: #0c0f12;
}
.totp-secret code {
  color: #e3c185; font-size: 15px; letter-spacing: .14em; word-break: break-all;
}
.totp-uri { word-break: break-all; }

/* The provider's own words about a refusal, under its status. Clipped to one
   line because the register is scanned down the status column; the whole
   sentence is on the row's title. */
.refusal-reason {
  display: block; margin-top: 4px; max-width: 22ch;
  color: var(--admin-muted); font-size: 8px; line-height: 1.4;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}

/* The failing-models table on the overview. Five columns, the last one wide:
   the provider's sentence is the column somebody actually reads. */
.admin-table-head.failing,
.admin-row.failing {
  grid-template-columns: 1.6fr .6fr .6fr .6fr 2.4fr;
}
.admin-row.failing { min-height: 46px; align-items: center; }
.admin-row.failing.over { background: rgba(181, 48, 48, .035); }
.failing-note { margin: 14px 0 20px; }

/* The sale-price table: six columns, two of them editable. Rows are forms, so
   they need the row's grid rather than a form's default block layout. */
.admin-table-head.sale,
.admin-row.sale {
  grid-template-columns: 1.8fr .8fr .8fr .9fr 1fr 52px;
}
.admin-row.sale { min-height: 52px; align-items: center; gap: 8px; }
.admin-row.sale input[type="text"] {
  width: 100%; height: 34px; padding: 0 9px;
  border: 1px solid var(--admin-border); border-radius: 7px;
  background: #0c0f12; color: #ccd2d7; font-size: 10px;
}
.admin-row.sale button { min-height: 34px; padding: 0 10px; }
.sale-panel { margin-top: 13px; padding: 24px; }

/* A key on screen for the only time it will ever be readable. Wide and
   selectable, because the next step is somebody copying it. */
.key-once { border-color: rgba(62, 202, 135, .22); background: rgba(40, 171, 108, .05); color: #7cc29e; }
.key-once-value {
  display: block; margin-top: 10px; padding: 12px 14px;
  border: 1px solid var(--admin-border); border-radius: 9px;
  background: #0c0f12; color: #e3c185;
  font-size: 13px; letter-spacing: .04em; word-break: break-all; user-select: all;
}
.new-customer { margin-bottom: 13px; }
.new-customer .tariff-fields label { display: flex; flex-direction: column; gap: 6px; }
.new-customer .tariff-fields button { grid-column: 1 / -1; }

/* The bulk-grant form: a number, a checkbox and a button on one line. */
.bulk-grant { margin-bottom: 13px; }
.bulk-grant .tariff-fields { grid-template-columns: 1fr 1.4fr auto; align-items: end; }
.bulk-grant .tariff-fields label { display: flex; flex-direction: column; gap: 6px; }
.bulk-grant .bulk-free { flex-direction: row !important; align-items: center; gap: 9px; height: 44px; }
.bulk-grant .bulk-free input { width: auto; height: auto; margin: 0; }

/* The price journal: five columns, the middle one carrying the badges. */
.admin-table-head.history,
.admin-row.history {
  grid-template-columns: .7fr 1.6fr 1.4fr .8fr .8fr;
}
.admin-row.history { min-height: 44px; align-items: center; }
.admin-row.history.over { background: rgba(181, 48, 48, .035); }

/* One customer's keys: prefix, two dates, a state and the revoke button. */
.admin-table-head.keys,
.admin-row.keys {
  grid-template-columns: 1.8fr .9fr 1fr .9fr auto;
}
.admin-row.keys { min-height: 48px; align-items: center; }

/* The launch checklist: what is missing, why it matters, and where to fix it.
   Only the unfinished items are drawn — a list of ticks is furniture. */
.readiness-panel { margin-bottom: 13px; }
.readiness-note { margin: 14px 0 4px; }
.admin-row.readiness {
  display: grid; grid-template-columns: 1.3fr 2.4fr auto;
  min-height: 56px; align-items: center; gap: 14px;
}
.admin-row.readiness.over { background: rgba(181, 48, 48, .035); }
.admin-row.readiness strong { font-size: 10px; }
.admin-row.readiness svg { vertical-align: -3px; margin-right: 6px; color: #d7a34b; }
.admin-row.readiness.over svg { color: #e07878; }
