/* The Airbrx brand system, hand-written — shared by every warehouse app.
 *
 * Part of the Airbrx UI kit, published alongside the kit's modules and pinned
 * to the same version line. A kit module and this file are coupled: `dom.js`
 * emits `.stat`, `chart.js` emits `.chart-wrap`, `analytics.js` emits
 * `.gateway-strip`. Load both from the same prefix — never one pinned and the
 * other moving.
 *
 * This began as two stylesheets, one per app, sharing the same tokens
 * character-for-character and ~80 of ~110 component selectors. They had
 * already drifted where it shows: the masked-token field was `.input-secret`
 * in one app and `.secret` in the other, with different declarations.
 *
 * Both apps rendered this design through Tailwind once: a build step, a config
 * file and ~20 dev dependencies to produce a stylesheet this size. The tokens
 * below are the same ones, stated once.
 *
 * Orange is the only accent. Status colors carry meaning, never decoration.
 *
 * Tokens and component values are kept in line with airbrx-admin's design
 * system (AIR-1677). airbrx-admin isn't itself fully consistent — it has three
 * different ad hoc table styles and six different badge/pill components across
 * its pages — so the single unified `.table` and `.tag` systems here were kept
 * rather than forked to mirror that fragmentation; only their
 * radius/padding/header treatment were tuned to match admin's most common
 * pattern within each family. The page background matches admin's pure white
 * exactly (not a tinted off-white) — cards read as "raised" via border +
 * shadow alone, the same way admin's do. Deliberate divergences, noted rather
 * than silently drifted:
 *   - No dark mode. Admin has full prefers-color-scheme + .dark-mode support;
 *     adding it here would touch every component in this file.
 *   - Icons are hand-drawn inline SVG (icons.js), not Font Awesome. Admin
 *     loads an icon font across ~48 files; the flat-stack rules explicitly
 *     avoid adding dependencies, so the SVG set (same 24x24 grid, 2px round
 *     stroke) stays rather than pulling in a font.
 *
 * An app keeps its own stylesheet for what only it has, loaded after this one.
 */

:root {
  --accent: #fd6c1d;
  --accent-light: #ff8c42;
  --accent-dark: #e65a0d;
  --accent-gradient: linear-gradient(135deg, #fd6c1d 0%, #ff8c42 100%);
  --accent-tint: rgba(253, 108, 29, 0.08);
  --accent-tint2: rgba(253, 108, 29, 0.14);
  --accent-shadow: rgba(253, 108, 29, 0.25);
  --accent-shadow-strong: rgba(253, 108, 29, 0.35);

  --ink: #1a1a1a;
  --body: #505050;
  --muted: #8a8a8a;
  --surface: #f5f5f5;
  --white: #fff;
  --border: rgba(0, 0, 0, 0.06);
  --border-strong: rgba(0, 0, 0, 0.12);

  --ok: #16a34a;
  --warn: #d97706;
  --urgent: #dc2626;
  --info: #2563eb;

  --radius: 12px;
  --radius-lg: 20px;
  --shadow: rgba(0, 0, 0, 0.04);
  --shadow-strong: rgba(0, 0, 0, 0.08);
  --shadow-card: 0 2px 8px var(--shadow), 0 0 1px rgba(0, 0, 0, 0.08);

  /* Inter if the machine has it, the platform UI face otherwise. Deliberately
     NOT fetched from fonts.googleapis.com: that is a third origin to trust on
     every page load, for a face the fallback stack already approximates.
     The two apps disagreed about this — one linked Google Fonts and one
     claimed in a comment to self-host Inter while shipping no font files at
     all, so the same design rendered in two different typefaces. Neither now
     loads a remote font, so they match. If Inter is ever wanted for certain,
     the woff2 files belong on this same kit prefix behind an @font-face here,
     where both apps pick them up at once. */
  --sans: "Inter", system-ui, -apple-system, sans-serif;
  --mono: "SF Mono", Menlo, Consolas, monospace;
}

*,
*::before,
*::after {
  box-sizing: border-box;
}

body {
  margin: 0;
  font-family: var(--sans);
  font-size: 15px;
  line-height: 1.6;
  color: var(--ink);
  background: var(--white);
  -webkit-font-smoothing: antialiased;
}

h1,
h2,
h3,
h4 {
  margin: 0;
  letter-spacing: -0.02em;
  line-height: 1.25;
}
h1 {
  font-size: 24px;
  font-weight: 700;
}
h2 {
  font-size: 22px;
  font-weight: 700;
}
h3 {
  font-size: 17px;
  font-weight: 700;
}
h4 {
  font-size: 13px;
  font-weight: 700;
}
p {
  margin: 0;
}
a {
  color: var(--accent-dark);
}

code,
.mono {
  font-family: var(--mono);
  font-size: 0.86em;
  letter-spacing: 0.02em;
}
code {
  background: var(--surface);
  padding: 0.1em 0.35em;
  border-radius: 5px;
}

:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}

/* ── layout ─────────────────────────────────────────────────────────── */

main {
  max-width: 1200px;
  margin: 0 auto;
  padding: 28px 24px 64px;
}

.stack > * + * {
  margin-top: 16px;
}
.stack-lg > * + * {
  margin-top: 28px;
}
.stack-sm > * + * {
  margin-top: 10px;
}

.row {
  display: flex;
  align-items: center;
  gap: 8px;
}
.row-between {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  flex-wrap: wrap;
}
.row-end {
  display: flex;
  justify-content: flex-end;
  gap: 8px;
}
.grow {
  flex: 1;
}
.full {
  width: 100%;
}

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

.lede {
  color: var(--body);
  max-width: 68ch;
}
.hint {
  font-size: 12px;
  color: var(--muted);
}
.fineprint {
  font-size: 11px;
  color: var(--muted);
}
.danger-text {
  color: var(--urgent);
}

h2.section-title {
  display: flex;
  align-items: center;
  gap: 10px;
  text-transform: uppercase;
  font-size: 10px;
  font-weight: 700;
  letter-spacing: 0.12em;
  color: var(--muted);
}
h2.section-title::after {
  content: "";
  flex: 1;
  height: 1px;
  background: var(--border);
}

/* ── top bar ────────────────────────────────────────────────────────── */

/* Brand + nav + connections live in one row (airbrx-admin's
   .app-header-inner shape) rather than a brand/utility bar with a separate
   sticky tab strip underneath — AIR-1677. */
.topbar {
  position: sticky;
  top: 0;
  z-index: 20;
  display: flex;
  align-items: center;
  gap: 16px;
  padding: 10px 24px;
  background: var(--white);
  border-bottom: 1px solid var(--border);
}
.brand {
  display: flex;
  align-items: center;
  background: none;
  border: 0;
  padding: 0;
  cursor: pointer;
  flex-shrink: 0;
}
.brand img {
  display: block;
  height: 26px;
  width: auto;
  user-select: none;
}
.topbar-right {
  display: flex;
  align-items: center;
  gap: 8px;
  margin-left: auto;
  flex-shrink: 0;
}

/* Connection buttons name the product, not the noun. */
.conn-btn {
  font: inherit;
  font-size: 14px;
  font-weight: 500;
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 6px 12px;
  background: var(--white);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  color: var(--ink);
  cursor: pointer;
  transition: border-color 0.12s, background-color 0.12s, color 0.12s;
}
.conn-btn:hover {
  border-color: var(--accent);
  background: var(--accent-tint);
  color: var(--accent-dark);
}
.conn-btn .detail {
  color: var(--muted);
  font-weight: 400;
  max-width: 160px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.dot {
  width: 7px;
  height: 7px;
  border-radius: 50%;
  background: var(--border-strong);
  flex: 0 0 auto;
}
.dot.live {
  background: var(--ok);
}

@media (max-width: 760px) {
  .conn-btn .detail {
    display: none;
  }
  .topbar {
    flex-wrap: wrap;
  }
  .tabs {
    order: 3;
    width: 100%;
    overflow-x: auto;
  }
}

/* ── tab rail ───────────────────────────────────────────────────────── */

.tabs {
  display: flex;
  align-items: center;
  gap: 4px;
}
.tab {
  font: inherit;
  font-size: 14px;
  font-weight: 500;
  display: flex;
  align-items: center;
  gap: 6px;
  padding: 8px 12px;
  border-radius: var(--radius);
  background: none;
  border: 0;
  color: var(--muted);
  cursor: pointer;
  transition: background-color 0.12s, color 0.12s;
}
.tab:hover {
  background: var(--accent-tint);
  color: var(--accent);
}
.tab[aria-selected="true"] {
  background: var(--accent-tint);
  color: var(--ink);
  font-weight: 600;
}
.tab[data-locked="true"] {
  color: rgba(26, 26, 26, 0.3);
}
.tab[data-locked="true"]:hover {
  background: none;
  color: rgba(26, 26, 26, 0.5);
}
.tab-lock {
  font-size: 10px;
  opacity: 0.7;
}

/* ── cards, fields ──────────────────────────────────────────────────── */

.card {
  background: var(--white);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-card);
  padding: 22px;
}
.card.flush {
  padding: 0;
  overflow: hidden;
}
.card.tight {
  padding: 14px 18px;
}

/* Hero panel for a page's primary empty/setup state — icon badge + heading +
   body, all inside one raised card (mirrors airbrx-admin's dashboard.css
   .state-card, used for "Connect your warehouse" / "Wire up a client"). */
.state-card {
  display: flex;
  flex-direction: column;
  gap: 20px;
  padding: 28px;
}
.state-head {
  display: flex;
  align-items: flex-start;
  gap: 16px;
}
.state-icon {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 44px;
  height: 44px;
  flex-shrink: 0;
  border-radius: var(--radius);
  background: var(--accent-gradient);
  color: var(--white);
  box-shadow: 0 4px 12px var(--accent-shadow);
}
.state-head-body {
  flex: 1;
  min-width: 0;
}

.field {
  border: 0;
  padding: 0;
  margin: 0;
  min-width: 0;
  display: block;
}
.field > label,
.field > legend {
  display: block;
  font-size: 10px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.12em;
  color: var(--muted);
  margin-bottom: 6px;
  padding: 0;
}
.field > .hint,
.field > .fineprint {
  margin-top: 6px;
}

.input {
  width: 100%;
  font: inherit;
  font-size: 14px;
  color: var(--ink);
  background: var(--white);
  border: 1px solid var(--border-strong);
  border-radius: var(--radius);
  padding: 9px 12px;
}
.input::placeholder {
  color: var(--muted);
}
.input:focus-visible {
  outline: none;
  border-color: var(--accent);
  box-shadow: 0 0 0 3px var(--accent-tint);
}
.input[aria-invalid="true"] {
  border-color: var(--urgent);
}
select.input {
  cursor: pointer;
  appearance: none;
  background-image: linear-gradient(45deg, transparent 50%, var(--muted) 50%), linear-gradient(135deg, var(--muted) 50%, transparent 50%);
  background-position: calc(100% - 16px) 50%, calc(100% - 11px) 50%;
  background-size: 5px 5px, 5px 5px;
  background-repeat: no-repeat;
  padding-right: 32px;
}
.input.mono {
  font-family: var(--mono);
  font-size: 12.5px;
}

/* Token fields mask in CSS instead of using type="password". A password
   field is a standing invitation to the browser: generate a strong password
   here, save this to the password manager, autofill a saved login — all
   wrong for a PAT that expires in a day. A text input gets none of that,
   and its `autocomplete: off` is actually honoured. Supported by Chromium,
   Safari, and Firefox 132+; an older Firefox shows the token in clear,
   which beats teaching the password manager to hoard expired PATs. */
/* One name, `.input-secret`. The Snowflake app called this `.secret` and
   carried three declarations the Databricks copy lacked; all four are kept —
   the standard property alongside the prefixed one, and mono + tracking so a
   masked token's dot count is countable when someone is checking a paste. */
.input-secret {
  -webkit-text-security: disc;
  text-security: disc;
  font-family: var(--mono);
  letter-spacing: 0.08em;
}

/* An input with a leading icon slot. */
.input-icon {
  position: relative;
}
.input-icon .icon {
  position: absolute;
  left: 11px;
  top: 50%;
  transform: translateY(-50%);
  color: var(--muted);
  pointer-events: none;
  line-height: 0;
}
.input-icon .input {
  padding-left: 34px;
}

/* ── buttons ────────────────────────────────────────────────────────── */

.btn-primary,
.btn-secondary,
.btn-ghost,
.btn-danger {
  font: inherit;
  font-size: 14px;
  font-weight: 600;
  border-radius: var(--radius);
  padding: 10px 18px;
  cursor: pointer;
  border: 1.5px solid transparent;
  text-decoration: none;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  transition: background-color 0.12s, border-color 0.12s, color 0.12s, transform 0.12s, box-shadow 0.12s;
}
.btn-primary:hover:not(:disabled),
.btn-secondary:hover:not(:disabled),
.btn-ghost:hover:not(:disabled),
.btn-danger:hover:not(:disabled) {
  transform: translateY(-1px);
}
.btn-primary {
  background: var(--accent-gradient);
  color: var(--white);
  box-shadow: 0 2px 8px var(--accent-shadow);
}
.btn-primary:hover:not(:disabled) {
  box-shadow: 0 6px 16px var(--accent-shadow-strong);
}
.btn-secondary {
  background: var(--white);
  border-color: var(--border-strong);
  color: var(--ink);
}
.btn-secondary:hover:not(:disabled) {
  background: var(--surface);
  box-shadow: 0 4px 10px var(--shadow-strong);
}
.btn-danger {
  background: var(--urgent);
  color: var(--white);
}
.btn-danger:hover:not(:disabled) {
  opacity: 0.9;
  box-shadow: 0 4px 10px var(--shadow-strong);
}
.btn-ghost {
  background: var(--white);
  color: var(--muted);
  font-weight: 500;
  font-size: 12px;
  padding: 6px 10px;
  border-color: var(--border);
}
.btn-ghost:hover:not(:disabled) {
  color: var(--ink);
  box-shadow: 0 4px 10px var(--shadow-strong);
}
/* A button that reads as a link — for demoting an alternative path below the
   primary actions ("use a personal access token instead"). */
.btn-link {
  font: inherit;
  font-size: 13px;
  background: none;
  border: none;
  padding: 0;
  color: var(--muted);
  text-decoration: underline;
  text-underline-offset: 2px;
  cursor: pointer;
}
.btn-link:hover {
  color: var(--ink);
}
button:disabled,
a[aria-disabled="true"] {
  opacity: 0.55;
  cursor: not-allowed;
  pointer-events: none;
}
.btn-primary:disabled {
  background: var(--border);
  color: var(--muted);
  opacity: 1;
}

/* Icon-only affordance inside a row (edit, remove). */
.icon-btn {
  font: inherit;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  padding: 6px;
  background: none;
  border: 0;
  border-radius: 8px;
  color: var(--muted);
  cursor: pointer;
  line-height: 0;
}
.icon-btn:hover {
  background: var(--surface);
  color: var(--ink);
}
.icon-btn.danger:hover {
  color: var(--urgent);
}

.button-row {
  display: flex;
  gap: 8px;
  flex-wrap: wrap;
}
.button-row > * {
  flex: 1 1 auto;
}

.segmented {
  display: flex;
  gap: 8px;
  flex-wrap: wrap;
}
.segmented button {
  font: inherit;
  font-size: 14px;
  font-weight: 500;
  padding: 8px 16px;
  border-radius: var(--radius);
  border: 1px solid var(--border-strong);
  background: var(--white);
  color: var(--muted);
  cursor: pointer;
}
.segmented button[aria-pressed="true"] {
  border-color: var(--accent);
  background: var(--accent-tint);
  color: var(--ink);
  font-weight: 600;
}

/* ── alerts, states ─────────────────────────────────────────────────── */

.alert {
  border-radius: var(--radius);
  padding: 12px 14px;
  font-size: 13px;
  border: 1px solid;
}
.alert-error {
  color: var(--urgent);
  background: rgba(220, 38, 38, 0.05);
  border-color: rgba(220, 38, 38, 0.3);
}
.alert-warn {
  color: var(--body);
  background: rgba(217, 119, 6, 0.06);
  border-color: rgba(217, 119, 6, 0.3);
}
.alert-info {
  color: var(--body);
  background: var(--accent-tint);
  border-color: rgba(253, 108, 29, 0.3);
}

.empty {
  border: 1px dashed var(--border-strong);
  border-radius: var(--radius);
  background: rgba(255, 255, 255, 0.5);
  padding: 32px;
  text-align: center;
  font-size: 14px;
  color: var(--muted);
}

.spinner {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 40px 0;
  font-size: 14px;
  color: var(--muted);
}
.spinner .spin {
  width: 16px;
  height: 16px;
  border-radius: 50%;
  border: 2px solid var(--border);
  border-top-color: var(--accent);
  animation: spin 0.7s linear infinite;
}
@keyframes spin {
  to {
    transform: rotate(360deg);
  }
}
@media (prefers-reduced-motion: reduce) {
  .spinner .spin {
    animation-duration: 2.4s;
  }
}

/* ── tags ───────────────────────────────────────────────────────────── */

.tag {
  display: inline-block;
  white-space: nowrap;
  border-radius: 999px;
  padding: 2px 8px;
  font-size: 11px;
  font-weight: 600;
}
.tag-neutral {
  background: var(--surface);
  color: var(--muted);
}
.tag-ok {
  background: rgba(22, 163, 74, 0.1);
  color: var(--ok);
}
.tag-warn {
  background: rgba(217, 119, 6, 0.1);
  color: var(--warn);
}
.tag-urgent {
  background: rgba(220, 38, 38, 0.1);
  color: var(--urgent);
}
.tag-info {
  background: rgba(37, 99, 235, 0.1);
  color: var(--info);
}

/* ── modal ──────────────────────────────────────────────────────────── */

/* Two dialogs, not one: the gateway and the workspace are independent
   connections, so each is managed on its own. */
.modal-backdrop {
  position: fixed;
  inset: 0;
  z-index: 50;
  display: flex;
  align-items: flex-start;
  justify-content: center;
  overflow-y: auto;
  padding: 16px;
  background: rgba(26, 26, 26, 0.4);
  backdrop-filter: blur(3px);
}
.modal {
  margin-top: 6vh;
  width: 100%;
  max-width: 560px;
  background: var(--white);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  box-shadow: 0 8px 24px rgba(0, 0, 0, 0.1), 0 0 1px rgba(0, 0, 0, 0.08);
}
.modal-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 16px 20px;
  border-bottom: 1px solid var(--border);
}
.modal-body {
  max-height: 74vh;
  overflow-y: auto;
  padding: 20px;
}
.modal-section + .modal-section {
  margin-top: 24px;
  padding-top: 20px;
  border-top: 1px solid var(--border);
}

/* The Terms of Service, rendered from the markdown the API serves. Its own
   scroll box rather than the modal body's, so the accept button stays in view
   while the document is read — and so "scrolled to the end" is a question that
   can be answered about the document itself. */
.tos-doc {
  max-height: 44vh;
  overflow-y: auto;
  padding: 14px 16px;
  border: 1px solid var(--border-strong);
  border-radius: var(--radius);
  background: var(--surface);
  font-size: 0.92rem;
  line-height: 1.55;
  color: var(--body);
}
.tos-doc > * + * {
  margin-top: 10px;
}
.tos-doc h3,
.tos-doc h4,
.tos-doc h5,
.tos-doc h6 {
  margin-top: 18px;
  color: var(--ink);
}
.tos-doc > :first-child {
  margin-top: 0;
}
.tos-doc ul,
.tos-doc ol {
  padding-left: 20px;
}
.tos-doc li + li {
  margin-top: 4px;
}
.tos-doc blockquote {
  margin-left: 0;
  padding-left: 12px;
  border-left: 3px solid var(--border-strong);
  color: var(--muted);
}
.tos-doc pre {
  overflow-x: auto;
  margin: 0;
  padding: 10px 12px;
  border-radius: 8px;
  background: var(--white);
}
.tos-doc hr {
  border: 0;
  border-top: 1px solid var(--border-strong);
}

/* ── connection rows ────────────────────────────────────────────────── */

.conn-list {
  overflow: hidden;
  border: 1px solid var(--border);
  border-radius: var(--radius);
}
.conn-row {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 10px 12px;
  border-bottom: 1px solid var(--border);
  background: var(--white);
}
.conn-row:last-child {
  border-bottom: 0;
}
.conn-row[data-active="true"] {
  background: var(--accent-tint);
}
.conn-row[data-disabled="true"] {
  opacity: 0.55;
}
.conn-pick {
  font: inherit;
  text-align: left;
  display: flex;
  align-items: center;
  gap: 12px;
  flex: 1;
  min-width: 0;
  background: none;
  border: 0;
  padding: 0;
  cursor: pointer;
}
.conn-pick:disabled {
  cursor: not-allowed;
}
.radio {
  width: 16px;
  height: 16px;
  flex: 0 0 auto;
  border-radius: 50%;
  border: 1px solid var(--border-strong);
  background: var(--white);
  display: inline-flex;
  align-items: center;
  justify-content: center;
}
.conn-row[data-active="true"] .radio {
  border-color: var(--accent);
  background: var(--accent);
  color: var(--white);
}
.conn-text {
  min-width: 0;
}
.conn-title {
  display: flex;
  align-items: center;
  gap: 8px;
  font-size: 13px;
  font-weight: 600;
  color: var(--ink);
}
.conn-sub {
  display: block;
  font-family: var(--mono);
  font-size: 11px;
  color: var(--muted);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* The connect form sits inside the dialog and is deliberately louder than the
   list above it — it is the thing to do when nothing is connected yet. */
.connect-form {
  border: 1.5px solid rgba(253, 108, 29, 0.4);
  background: var(--accent-tint);
  border-radius: var(--radius);
  padding: 16px;
}
.connect-form.plain {
  border: 1px solid var(--border);
  background: rgba(245, 245, 245, 0.4);
}
.connect-form > * + * {
  margin-top: 12px;
}

/* ── stats ──────────────────────────────────────────────────────────── */

.stats {
  display: grid;
  gap: 12px;
  grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
}
.stat {
  position: relative;
  overflow: hidden;
  background: var(--white);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  box-shadow: var(--shadow-card);
  padding: 18px;
}
.stat::before {
  content: "";
  position: absolute;
  inset: 0 0 auto 0;
  height: 3px;
  background: var(--accent);
}
.stat .label {
  font-size: 10px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.12em;
  color: var(--muted);
}
.stat .value {
  margin-top: 8px;
  font-size: 30px;
  font-weight: 800;
  line-height: 1;
  letter-spacing: -0.03em;
  color: var(--accent);
}
.stat.plain .value {
  color: var(--ink);
}
.stat .context {
  margin-top: 6px;
  font-size: 11px;
  color: var(--muted);
}

/* ── tables ─────────────────────────────────────────────────────────── */

/* Wide content scrolls inside its own box — the page never scrolls sideways. */
.table-scroll {
  overflow-x: auto;
}
.table {
  width: 100%;
  border-collapse: collapse;
  font-size: 13px;
}
.table th,
.table td {
  padding: 10px 14px;
  text-align: left;
  border-bottom: 1px solid var(--border);
  vertical-align: top;
}
.table thead th {
  font-size: 11px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  color: var(--muted);
  white-space: nowrap;
}
.table tbody tr:last-child td {
  border-bottom: 0;
}
.table .right {
  text-align: right;
  font-variant-numeric: tabular-nums;
}
.table .sql {
  font-family: var(--mono);
  font-size: 11.5px;
  color: var(--body);
}
.table.compact th,
.table.compact td {
  padding: 6px 10px;
}

/* ── rules ──────────────────────────────────────────────────────────── */

.rules {
  border: 1px solid var(--border);
  border-radius: var(--radius);
  overflow: hidden;
  background: var(--white);
}
.rule {
  display: flex;
  align-items: flex-start;
  gap: 12px;
  padding: 12px 16px;
  border-bottom: 1px solid var(--border);
}
.rule:last-child {
  border-bottom: 0;
}
.rule[data-selected="false"] {
  opacity: 0.55;
}
.rule-priority {
  width: 24px;
  padding-top: 2px;
  text-align: right;
  font-family: var(--mono);
  font-size: 12px;
  font-variant-numeric: tabular-nums;
  color: var(--muted);
}
.rule-main {
  flex: 1;
  min-width: 0;
}
.rule-head {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 8px;
}
.rule-name {
  font-size: 14px;
  font-weight: 600;
  color: var(--ink);
}
.rule-desc {
  margin-top: 4px;
  font-size: 13px;
  color: var(--body);
}
.rule-rationale {
  margin-top: 6px;
  font-size: 12px;
  font-style: italic;
  color: var(--muted);
}
.chips {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
  margin-top: 6px;
}
.chip {
  border-radius: 8px;
  background: var(--surface);
  padding: 2px 6px;
  font-family: var(--mono);
  font-size: 11px;
  color: var(--muted);
}
.tag-cache {
  background: var(--accent-tint2);
  color: var(--accent-dark);
}
.tag-bypass {
  background: rgba(37, 99, 235, 0.1);
  color: var(--info);
}
.rule-impact {
  text-align: right;
  white-space: nowrap;
  font-size: 12px;
  color: var(--muted);
  padding-top: 2px;
}
.rule-impact .big {
  font-weight: 600;
  color: var(--accent);
}

/* A switch, not a checkbox: it toggles whether a rule ships. */
.switch {
  flex: 0 0 auto;
  margin-top: 4px;
  width: 36px;
  height: 20px;
  padding: 2px;
  border: 0;
  border-radius: 999px;
  background: var(--border-strong);
  cursor: pointer;
  transition: background-color 0.12s;
}
.switch[aria-checked="true"] {
  background: var(--accent);
}
.switch span {
  display: block;
  width: 16px;
  height: 16px;
  border-radius: 50%;
  background: var(--white);
  transition: transform 0.12s;
}
.switch[aria-checked="true"] span {
  transform: translateX(16px);
}

/* ── gateway strip ──────────────────────────────────────────────────── */

/* Clients → Airbrx → warehouse pill diagram, ported from airbrx-admin's
   .gateway-strip (tenants.html). The Airbrx node doubles as the copy
   affordance for the gateway FQDN. */
.gateway-strip {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: center;
  gap: 12px;
  padding: 12px 16px;
}
.gateway-node {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  padding: 8px 16px;
  border: 1px solid var(--border-strong);
  border-radius: 8px;
  background: var(--white);
  font-size: 13px;
  color: var(--ink);
}
.gateway-node--airbrx {
  background: var(--accent-gradient);
  border-color: transparent;
  color: var(--white);
  font-family: var(--mono);
  font-weight: 600;
  cursor: pointer;
}
.gateway-node--missing {
  border-style: dashed;
  color: var(--muted);
  background: var(--surface);
}
.gateway-node-stack {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 2px;
  text-align: center;
}
.gateway-node-label {
  font-size: 10px;
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--muted);
}
.gateway-node--airbrx .gateway-node-label {
  color: rgba(255, 255, 255, 0.85);
}
.gateway-arrow {
  color: var(--muted);
  font-size: 12px;
}
.gateway-copy-hint {
  opacity: 0.75;
  margin-left: 4px;
  font-size: 11px;
  font-family: var(--sans);
  font-weight: 500;
}
.gateway-node--airbrx:hover .gateway-copy-hint {
  opacity: 1;
}
.gateway-strip-hint {
  display: flex;
  align-items: center;
  gap: 8px;
  flex-wrap: wrap;
  padding: 10px 14px;
  border-radius: var(--radius);
  background: var(--surface);
  font-size: 13px;
  color: var(--body);
}

/* ── chart ──────────────────────────────────────────────────────────── */

/* The whole charting library, replaced. ~400 KB of recharts drew one line. */
.chart-wrap {
  position: relative;
}
.chart {
  display: block;
  user-select: none;
}
.chart .grid {
  stroke: var(--border);
  stroke-width: 1;
}
.chart .line {
  fill: none;
  stroke: var(--accent);
  stroke-width: 2;
  stroke-linejoin: round;
  stroke-linecap: round;
}
.chart .area {
  fill: var(--accent);
  fill-opacity: 0.1;
}
.chart .axis {
  font-size: 11px;
  fill: var(--muted);
}
.chart .axis.num {
  font-variant-numeric: tabular-nums;
}
/* The accent is 2.79:1 on white — below 3:1. The line alone must never be the
   only way to read a value, so the peak is labelled and a table sits below. */
.chart .peak-label {
  font-size: 11px;
  font-weight: 700;
  fill: var(--ink);
}
.chart .marker {
  fill: var(--accent);
  stroke: var(--white);
  stroke-width: 2;
}
.chart-tip {
  position: absolute;
  z-index: 10;
  pointer-events: none;
  transform: translateX(-50%);
  background: var(--white);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  box-shadow: var(--shadow-card);
  padding: 6px 10px;
  font-size: 12px;
}
.chart-tip .when {
  font-weight: 600;
  color: var(--ink);
}
.chart-tip .what {
  color: var(--muted);
}

/* ── disclosure ─────────────────────────────────────────────────────── */

details.help > summary {
  font-size: 12px;
  color: var(--muted);
  cursor: pointer;
}
details.help > summary:hover {
  color: var(--ink);
}
.help-body {
  margin-top: 10px;
  padding: 12px 14px;
  background: var(--surface);
  border-radius: var(--radius);
  font-size: 13px;
  color: var(--body);
}
.help-body > * + * {
  margin-top: 8px;
}
.help-body ul {
  margin: 8px 0 0;
  padding-left: 18px;
}
.help-body li + li {
  margin-top: 5px;
}
.bordered-top {
  border-top: 1px solid var(--border);
  padding-top: 12px;
}


/* ── shared components that had only ever shipped in one app ─────────── */

/* A sticky action bar for the bottom of a long results page. */
.actionbar {
  position: sticky;
  bottom: 0;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 14px;
  flex-wrap: wrap;
  padding: 14px 0;
  margin-top: 8px;
  background: linear-gradient(to top, var(--white) 72%, rgba(255, 255, 255, 0));
}

.results-head {
  display: flex;
  align-items: flex-end;
  justify-content: space-between;
  gap: 14px;
  flex-wrap: wrap;
}

.row-actions {
  display: flex;
  gap: 8px;
  align-items: center;
}

/* Full-width sign-in buttons. The provider marks are fixed-colour brand assets
   (icons.js `brand()`), never currentColor — see the note there. */
.btn-provider {
  width: 100%;
}
.btn-provider .provider-icon {
  width: 18px;
  height: 18px;
  flex: none;
}

.checklist {
  margin: 0;
  padding-left: 18px;
  font-size: 13px;
  color: var(--body);
}
.checklist li + li {
  margin-top: 6px;
}

/* Inline monospace run — SQL fragments, hostnames, identifiers. */
.code {
  font-family: var(--mono);
  font-size: 0.86em;
  letter-spacing: 0.02em;
}

.h3 {
  font-size: 17px;
  font-weight: 700;
}

.strong {
  font-weight: 700;
  color: var(--ink);
}

.optional {
  color: var(--muted);
  font-weight: 400;
}

/* A small "open this elsewhere" affordance inside a table row. */
.open-in {
  margin-top: 6px;
  padding: 3px 8px;
  font-size: 11px;
}

/* Connection status in the top bar, for an app that shows one warehouse
   rather than a pair of connection buttons. */
.conn {
  display: flex;
  align-items: center;
  gap: 7px;
  font-size: 12px;
  color: var(--muted);
  max-width: 42vw;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.conn .dot {
  width: 7px;
  height: 7px;
  border-radius: 50%;
  background: var(--ok);
  flex: 0 0 auto;
}

/* Rule-family tags. Two more members of the `.tag` system above. */
.tag-ttl {
  background: var(--surface);
  color: var(--muted);
  font-family: var(--mono);
}
.tag-invalidation {
  background: rgba(217, 119, 6, 0.1);
  color: var(--warn);
}

.footer {
  max-width: 1200px;
  margin: 0 auto;
  padding: 0 24px 40px;
  font-size: 12px;
  color: var(--muted);
}
