/* RebelText user console.
 *
 * One stylesheet, no build step, no framework. The look is the dashboard
 * design handed off in October 2026 (design_handoff_rebeltext_dashboard):
 * Geist for the interface, Geist Mono for IDs, keys and numbers, Barlow
 * Condensed for titles and figures, square corners, and the brand red as the
 * one accent — a hard offset shadow on the primary action.
 *
 * The operator console (internal/admin/static/admin.css) no longer shares
 * this palette; the portal moved on its own.
 *
 * The --rt-* tokens are the design's, named as it names them. The shorter
 * names under them (--bg, --text, --muted…) are what the rules below were
 * written against, kept as aliases so a rule reads the same either way.
 *
 * Dark is the default. Light applies when the OS asks for it or the customer
 * picks it from the top bar (portal.js sets data-theme on <html>). Only tokens
 * change between the two.
 *
 * The CSP is default-src 'self', so the web fonts cannot be pulled from
 * Google; they are served from /static instead (see the @font-face rules). It
 * also forbids an inline style attribute, so spacing that would be a
 * one-off style="" in the markup is a rule in here instead. */

/* The design's faces, served from here: the CSP allows no font host. Latin
   subsets taken from the design handoff's own bundle; anything outside them
   falls back down the stacks below. Geist, Geist Mono and JetBrains Mono are
   variable, one file for every weight. All four are OFL-licensed. */
@font-face { font-family: "Geist"; font-style: normal; font-weight: 100 900; font-display: swap; src: url("font-geist.woff2") format("woff2"); }
@font-face { font-family: "Geist Mono"; font-style: normal; font-weight: 100 900; font-display: swap; src: url("font-geist-mono.woff2") format("woff2"); }
@font-face { font-family: "JetBrains Mono"; font-style: normal; font-weight: 100 800; font-display: swap; src: url("font-jetbrains-mono.woff2") format("woff2"); }
@font-face { font-family: "Barlow Condensed"; font-style: normal; font-weight: 600; font-display: swap; src: url("font-barlow-condensed-600.woff2") format("woff2"); }
@font-face { font-family: "Barlow Condensed"; font-style: normal; font-weight: 700; font-display: swap; src: url("font-barlow-condensed-700.woff2") format("woff2"); }

:root {
  color-scheme: dark;

  --rt-bg: #0c0c0b;
  --rt-surface: #131312;
  --rt-surface2: #181817;
  --rt-line: #252422;
  --rt-line2: #33322e;
  --rt-ink: #eeeae0;
  --rt-mute: #a09b90;
  --rt-dim: #6c685f;
  --rt-accent: #f04a2e;
  --rt-accent-ink: #ff6e52;
  --rt-accent-bg: rgb(240 74 46 / 10%);
  --rt-alert-bg: rgb(240 74 46 / 7%);
  --rt-alert-line: rgb(240 74 46 / 32%);
  --rt-hover: #1a1a18;
  --rt-active: #1f1e1c;
  --rt-good: #6fcf8a;
  --rt-good-bg: rgb(111 207 138 / 10%);
  --rt-warn: #e5a846;
  --rt-warn-bg: rgb(229 168 70 / 12%);
  --rt-scrim: rgb(0 0 0 / 60%);
  --rt-pop-shadow: 0 16px 48px rgb(0 0 0 / 55%);

  /* The rules below were written against these. */
  --bg: var(--rt-bg);
  --surface: var(--rt-surface);
  --surface-2: var(--rt-surface2);
  --input: var(--rt-bg);
  --border: var(--rt-line);
  --border-strong: var(--rt-line2);
  --text: var(--rt-ink);
  --muted: var(--rt-mute);
  --faint: var(--rt-dim);
  --primary: var(--rt-accent);
  --primary-hover: #d83c1f;
  --rose: var(--rt-accent-ink);
  --on-text: var(--rt-bg);
  --danger: var(--rt-accent-ink);
  --danger-bg: var(--rt-alert-bg);
  --ok-bg: var(--rt-good-bg);
  --ok-text: var(--rt-good);
  --warn-bg: var(--rt-warn-bg);
  --warn-text: var(--rt-warn);
  --info-bg: var(--rt-hover);
  --info-text: var(--rt-mute);

  /* A panel: lit from the top, a step above the page. */
  --panel: linear-gradient(180deg, var(--rt-surface2), var(--rt-surface));
  /* The primary action's hard offset shadow, the design's signature. */
  --press: 3px 3px 0 rgb(240 74 46 / 35%);
  --shadow: var(--rt-pop-shadow);

  --sidebar-w: 216px;
  --topbar-h: 60px;
  /* Two heights for every field and button on every page: the standard
     one, and a small one for actions inside a table row or a dense list.
     Nothing else picks its own. */
  --control-h: 40px;
  --control-h-sm: 32px;
  /* The top bar's controls are standard controls too. */
  --topbar-control: var(--control-h);

  /* Which lockup file shows: white type on the dark ground, black on the
     light. A display value rather than a colour, since an <img> takes none. */
  --logo-on-dark: block;
  --logo-on-light: none;

  --radius: 0;

  --font-ui: "Geist", -apple-system, BlinkMacSystemFont, "Segoe UI", "Helvetica Neue", Arial, sans-serif;
  --font-mono: "Geist Mono", "JetBrains Mono", ui-monospace, "SF Mono", SFMono-Regular, Menlo, Consolas, monospace;
  --font-label: "JetBrains Mono", ui-monospace, "SF Mono", SFMono-Regular, Menlo, Consolas, monospace;
  --font-heading: "Barlow Condensed", "Avenir Next Condensed", "Roboto Condensed",
    "Arial Narrow", "Helvetica Neue", sans-serif;
}

/* The light palette, twice: once for an OS that prefers light and has not been
   overridden to dark, once for an explicit choice of light. CSS has no way to
   share one block between a media query and an attribute selector. */
@media (prefers-color-scheme: light) {
  :root:not([data-theme="dark"]) {
    color-scheme: light;

    --rt-bg: #f3f1ea;
    --rt-surface: #ffffff;
    --rt-surface2: #ffffff;
    --rt-line: #e1dccf;
    --rt-line2: #cbc4b2;
    --rt-ink: #16140f;
    --rt-mute: #57524a;
    --rt-dim: #86806f;
    --rt-accent: #e5401f;
    --rt-accent-ink: #bf3418;
    --rt-accent-bg: rgb(229 64 31 / 8%);
    --rt-alert-bg: #fff1ec;
    --rt-alert-line: #f0b9a7;
    --rt-hover: #f6f3ec;
    --rt-active: #eeeae0;
    --rt-good: #1c7536;
    --rt-good-bg: rgb(28 117 54 / 9%);
    --rt-warn: #9a5f00;
    --rt-warn-bg: rgb(196 128 10 / 12%);
    --rt-scrim: rgb(24 22 15 / 35%);
    --rt-pop-shadow: 0 16px 40px rgb(24 22 15 / 12%);
    --primary-hover: #cc3517;

    --logo-on-dark: none;
    --logo-on-light: block;
  }
}

:root[data-theme="light"] {
  color-scheme: light;

  --rt-bg: #f3f1ea;
  --rt-surface: #ffffff;
  --rt-surface2: #ffffff;
  --rt-line: #e1dccf;
  --rt-line2: #cbc4b2;
  --rt-ink: #16140f;
  --rt-mute: #57524a;
  --rt-dim: #86806f;
  --rt-accent: #e5401f;
  --rt-accent-ink: #bf3418;
  --rt-accent-bg: rgb(229 64 31 / 8%);
  --rt-alert-bg: #fff1ec;
  --rt-alert-line: #f0b9a7;
  --rt-hover: #f6f3ec;
  --rt-active: #eeeae0;
  --rt-good: #1c7536;
  --rt-good-bg: rgb(28 117 54 / 9%);
  --rt-warn: #9a5f00;
  --rt-warn-bg: rgb(196 128 10 / 12%);
  --rt-scrim: rgb(24 22 15 / 35%);
  --rt-pop-shadow: 0 16px 40px rgb(24 22 15 / 12%);
  --primary-hover: #cc3517;

  --logo-on-dark: none;
  --logo-on-light: block;
}

* { box-sizing: border-box; }

body {
  margin: 0;
  background: var(--bg);
  color: var(--text);
  font: 13px/1.55 var(--font-ui);
  -webkit-font-smoothing: antialiased;
}

::selection { background: var(--primary); color: #fff; }

code {
  font-family: var(--font-mono);
  font-size: 12px;
  color: var(--muted);
}

a { color: var(--text); }

p a { color: var(--rt-accent-ink); text-decoration: none; }
p a:hover { color: var(--text); }

h1, h2, h3 {
  font-family: var(--font-heading);
  text-transform: uppercase;
  line-height: 1;
  color: var(--text);
}

h1 { font-size: 36px; font-weight: 700; letter-spacing: 0.02em; line-height: 0.95; margin: 0 0 24px; }
h2 { font-size: 22px; font-weight: 700; letter-spacing: 0.02em; margin: 0 0 14px; }
h3 { font-size: 16px; font-weight: 700; letter-spacing: 0.02em; margin: 0 0 10px; }
/* An ID or key in a heading keeps its case: prefixes are case-sensitive. */
h1 code, h2 code, h3 code { text-transform: none; font-size: 0.6em; letter-spacing: 0; }

.muted { color: var(--muted); }
.small { font-size: 11px; }

/* A page's opening sentence, sitting under its title rather than inside the
   first card: it says what the whole page is for, so it belongs to none of
   them. The negative margin pulls it up under the heading, whose own bottom
   margin is sized for a card following it. Measured, because prose set the
   full width of the console is prose nobody finishes. */
.subtitle { margin: -14px 0 28px; max-width: 80ch; color: var(--muted); font-size: 13.5px; line-height: 1.6; }
.danger-text { color: var(--danger); }

/* Long values — endpoint URLs, error text from somebody else's server — break
   rather than stretching their column past the viewport. */
.wrap { overflow-wrap: anywhere; }
/* A value that reads as one thing — a timestamp — kept on one line. */
.nowrap { white-space: nowrap; }

/* A message's text in a log table: wide enough to read a line of it, no wider
   than a column should be, and cut after three lines so one long message does
   not make its row a page tall. Line breaks the sender typed are kept. */
.msg-body { min-width: 14rem; max-width: 28rem; overflow-wrap: anywhere; }
.msg-body .clamp {
  display: -webkit-box;
  -webkit-box-orient: vertical;
  -webkit-line-clamp: 3;
  line-clamp: 3;
  overflow: hidden;
  white-space: pre-line;
}
.msg-body .attach { display: block; }
/* The ID under a message's text; a sent message's opens its page. */
.msg-body .msg-id { display: block; width: fit-content; margin-top: 4px; }
.msg-body a.msg-id { text-decoration: underline; text-underline-offset: 3px; }
.msg-body a.msg-id:hover code { color: var(--primary); }

/* ── One message ─────────────────────────────────────────────────────────── */

/* The page title with its status beside it, and the way back to the log. */
.back { margin: 0 0 8px; }
.section-head { display: flex; align-items: center; gap: 14px; flex-wrap: wrap; }
.section-head h1 { margin-bottom: 0; }
.section-head + .subtitle { margin-top: 8px; overflow-wrap: anywhere; }

/* The text as sent: every line break kept, wrapped rather than scrolled, since
   it is prose and not JSON. */
pre.message-text { white-space: pre-wrap; overflow-wrap: anywhere; font-size: 14px; }
.card h3 { margin-top: 20px; font-size: 12px; }
.media-list { margin: 0; padding-left: 20px; }
.media-list li + li { margin-top: 4px; }

/* A message's life, one ruled step at a time, the marker coloured by how the
   step went. */
.timeline { list-style: none; margin: 0 0 12px; padding: 0; }
.timeline .tl {
  position: relative;
  padding: 10px 0 10px 22px;
  border-bottom: 1px solid var(--border);
}
.timeline .tl:last-child { border-bottom: none; }
.timeline .tl::before {
  content: "";
  position: absolute;
  left: 2px;
  top: 16px;
  width: 8px;
  height: 8px;
  border: 1px solid var(--muted);
  background: var(--surface);
}
.timeline .tl-ok::before { border-color: var(--ok-text); background: var(--ok-text); }
.timeline .tl-bad::before { border-color: var(--danger); background: var(--danger); }
.timeline .tl-pending::before { border-style: dashed; }
.tl-head { display: flex; justify-content: space-between; gap: 12px; align-items: baseline; }
.tl-at { white-space: nowrap; font-variant-numeric: tabular-nums; }

/* Screen-reader-only text. Used where a link's visible label is short enough to
   be ambiguous out of context — "View", repeated down a column of invoices. */
.visually-hidden {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}

/* ── Shell ───────────────────────────────────────────────────────────────── */

/* The sections down the side; the account as a whole — whether it is
   sending, its balance, the theme, who is signed in — along the top.

   A grid rather than a fixed-position aside, so the main column is laid out
   next to the sidebar instead of underneath it with a margin standing in. The
   sidebar spans both rows; the top bar and the page share the second column.

   No JavaScript collapses it. The CSP forbids inline script and a sidebar that
   needs a toggle to be usable is a sidebar that breaks with it blocked, so the
   narrow layout is a media query over the same markup. */
.shell {
  display: grid;
  grid-template-columns: var(--sidebar-w) minmax(0, 1fr);
  grid-template-rows: auto 1fr;
  grid-template-areas:
    "side top"
    "side main";
  min-height: 100vh;
  /* Without this the leftover space under a short page is shared out between
     the rows, which at narrow widths pushes the content halfway down the
     screen. */
  align-content: start;
}

.shell > main { grid-area: main; }

.sidebar {
  grid-area: side;
  position: sticky;
  top: 0;
  /* On a phone this element is the drawer, a popover. At this width the
     browser's popover styles are undone: it is not hidden, not fixed to the
     viewport and not boxed. */
  inset: 0 auto auto 0;
  width: auto;
  margin: 0;
  padding: 0;
  border: 0;
  border-right: 1px solid var(--border);
  color: inherit;
  align-self: start;
  z-index: 10;
  display: flex;
  flex-direction: column;
  height: 100vh;
  background: linear-gradient(180deg, var(--rt-surface2), var(--rt-bg));
  font-family: var(--font-label);
  overflow: hidden;
}

/* Keyboard users would otherwise tab the whole nav on every page. */
.skip {
  position: absolute;
  left: -9999px;
  z-index: 20;
  padding: 10px 16px;
  background: var(--text);
  color: var(--on-text);
}
.skip:focus { left: 8px; top: 8px; }

.brand, .login-brand {
  display: flex;
  align-items: center;
  text-decoration: none;
}

.sidebar-head {
  display: flex;
  align-items: center;
  flex: none;
  height: var(--topbar-h);
  padding: 0 18px;
  border-bottom: 1px solid var(--border);
}

/* What only a phone shows: the mark and the menu button in the top bar, and
   the drawer's own close button and account controls. */
.topbar-brand, .menu-toggle, .drawer-close, .drawer-account { display: none; }

.logo { width: auto; height: 24px; }
.logo.on-dark { display: var(--logo-on-dark); }
.logo.on-light { display: var(--logo-on-light); }

.sidebar nav {
  flex: 1;
  min-height: 0;
  overflow-y: auto;
  display: flex;
  flex-direction: column;
  gap: 2px;
  padding: 14px 10px;
}

/* A row: the icon in its own square, then the name. */
.sidebar nav a, .sidebar .nav-group > summary, .sidebar-foot a {
  display: flex;
  align-items: center;
  gap: 10px;
  height: 36px;
  padding: 0 6px;
  color: var(--muted);
  font-size: 13px;
  text-decoration: none;
  white-space: nowrap;
  transition: background 0.15s, color 0.15s;
}

.sidebar nav a:hover, .sidebar .nav-group > summary:hover, .sidebar-foot a:hover {
  color: var(--text);
  background: var(--rt-hover);
}

.nav-icon {
  display: flex;
  align-items: center;
  justify-content: center;
  flex: none;
  width: 26px;
  height: 26px;
  color: var(--faint);
}

/* The page being shown: the row lifted, and its icon on the brand red — the
   only red in the sidebar, so it is the one thing the eye finds. */
.sidebar nav > a.active, .sidebar .nav-group > summary.active {
  color: var(--text);
  background: var(--rt-active);
  font-weight: 500;
}
.sidebar nav > a.active .nav-icon,
.sidebar .nav-group > summary.active .nav-icon { background: var(--primary); color: #fff; }

/* A group's name: small, quiet, and not a link. */
.sidebar .nav-label {
  margin: 16px 8px 6px;
  color: var(--faint);
  font-size: 10px;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  white-space: nowrap;
}
.sidebar .nav-label:first-child { margin-top: 0; }

/* Billing and the pages under it. The children hang off a rule down the left,
   and the one being shown takes the red on its stretch of it. */
.nav-group > summary { cursor: pointer; list-style: none; }
.nav-group > summary::-webkit-details-marker { display: none; }
.nav-group .nav-text, .sidebar nav > a .nav-text { flex: 1; }
.nav-group .chev {
  width: 6px;
  height: 6px;
  margin-right: 6px;
  border-right: 1.5px solid var(--faint);
  border-bottom: 1.5px solid var(--faint);
  transform: rotate(-45deg);
  transition: transform 0.15s;
}
.nav-group[open] .chev { transform: rotate(45deg); }

.nav-group > a {
  height: 30px;
  margin: 0 0 0 22px;
  padding: 0 10px 0 22px;
  border-left: 1px solid var(--border);
  font-size: 12.5px;
}
.nav-group > a:first-of-type { margin-top: 2px; }
.nav-group > a:last-child { margin-bottom: 4px; }
.nav-group > a.active {
  color: var(--text);
  background: var(--rt-active);
  border-left-color: var(--primary);
  font-weight: 500;
}

.nav-badge {
  display: flex;
  align-items: center;
  height: 18px;
  padding: 0 6px;
  font-size: 10.5px;
  color: var(--muted);
  background: var(--rt-active);
}
.nav-badge.alert { color: var(--rt-accent-ink); background: var(--rt-accent-bg); }

/* The reference, kept apart at the foot: it leaves the portal. */
.sidebar-foot {
  flex: none;
  padding: 8px 10px 12px;
  border-top: 1px solid var(--border);
}
.sidebar-foot .nav-text { flex: 1; }
.sidebar-foot .ext { font-size: 12px; color: var(--faint); }

/* ── Top bar ─────────────────────────────────────────────────────────────── */

/* Sticky, so the balance is in view however far down a long table somebody
   has scrolled — it is the number that decides whether the next send goes
   out. */
.topbar {
  grid-area: top;
  position: sticky;
  top: 0;
  z-index: 9;
  display: flex;
  align-items: center;
  gap: 10px;
  height: var(--topbar-h);
  padding: 0 28px;
  background: var(--bg);
  border-bottom: 1px solid var(--border);
  font-family: var(--font-label);
}

/* Whether a message sent now would go out: a pulsing dot, green or red. The
   balance after it is pushed to the far end. */
.sending {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  height: 28px;
  margin-right: auto;
  padding: 0 10px;
  color: var(--text);
  font-size: 11px;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  white-space: nowrap;
}
.sending svg { flex: none; color: var(--faint); }
.sending-label { color: var(--faint); }
.sending .dot {
  position: relative;
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: var(--rt-good);
  box-shadow: 0 0 8px var(--rt-good);
}
.sending.paused .dot { background: var(--primary); box-shadow: 0 0 8px var(--primary); animation: rt-blink 1.4s ease-in-out infinite; }
.sending .dot::after {
  content: "";
  position: absolute;
  inset: 0;
  border-radius: 50%;
  background: inherit;
  animation: rt-pulse 2.4s ease-out infinite;
}
.sending.paused .dot::after { animation-duration: 1.4s; }
/* VOLT_FAKE: amber, the colour of "not the real thing yet". */
.sending.fake .dot { background: var(--warn-text); box-shadow: 0 0 8px var(--warn-text); }
.sending.fake .sending-state { color: var(--warn-text); }
/* Its hint hangs from the left: the indicator sits at the start of the bar. */
.sending[data-tip]:hover::after, .sending[data-tip]:focus-visible::after { left: 0; right: auto; }

@keyframes rt-pulse {
  0% { transform: scale(1); opacity: 0.9; }
  70%, 100% { transform: scale(2.4); opacity: 0; }
}
@keyframes rt-blink { 50% { opacity: 0.35; } }

/* The balance: a label and the number, the way to its history, and the top-up
   button fastened to its end. Washed red once it has run out. */
.balance {
  display: flex;
  align-items: stretch;
  height: var(--topbar-control);
  margin-left: auto;
  border: 1px solid var(--border);
}
.sending + .balance { margin-left: 0; }
.balance.low { border-color: var(--rt-alert-line); background: var(--rt-alert-bg); }

.balance-link {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 0 12px;
  color: var(--text);
  text-decoration: none;
}
.balance-link:hover { background: var(--rt-hover); }

.balance-label {
  color: var(--muted);
  font-size: 10.5px;
  letter-spacing: 0.14em;
  text-transform: uppercase;
}

.balance-value {
  font-family: var(--font-heading);
  font-weight: 700;
  font-size: 21px;
  line-height: 1;
  font-variant-numeric: tabular-nums;
}
.balance.low .balance-value { color: var(--rt-accent-ink); }

.balance-add {
  display: flex;
  align-items: center;
  gap: 7px;
  margin: 3px;
  padding: 0 10px;
  background: var(--primary);
  color: #fff;
  font-family: var(--font-ui);
  font-size: 12px;
  font-weight: 500;
  text-decoration: none;
  box-shadow: 2px 2px 0 rgb(240 74 46 / 35%);
  transition: transform 0.12s, box-shadow 0.12s;
}
.balance-add:hover, .balance-add.active { transform: translate(-1px, -1px); box-shadow: var(--press); }

/* Who is signed in, and the menu behind their initials. */
.account-menu { position: relative; }

.account-menu summary {
  display: flex;
  align-items: center;
  gap: 8px;
  height: var(--topbar-control);
  padding: 0 9px 0 5px;
  border: 1px solid var(--border);
  color: var(--muted);
  cursor: pointer;
  list-style: none;
}
.account-menu summary::-webkit-details-marker { display: none; }
.account-menu summary:hover,
.account-menu[open] summary { color: var(--text); border-color: var(--border-strong); }
.account-menu summary.active { border-bottom-color: var(--primary); }
.account-menu summary:focus-visible { outline: 2px solid var(--primary); outline-offset: 2px; }

.avatar {
  display: flex;
  align-items: center;
  justify-content: center;
  flex: none;
  width: 24px;
  height: 24px;
  border-radius: 50%;
  background: var(--rt-active);
  color: var(--text);
  font-size: 10px;
  font-weight: 600;
}
.avatar.large {
  width: 36px;
  height: 36px;
  border-radius: 0;
  background: var(--primary);
  color: #fff;
  font-family: var(--font-heading);
  font-size: 18px;
  font-weight: 700;
}

.caret {
  width: 5px;
  height: 5px;
  margin: -3px 2px 0;
  border-right: 1.5px solid var(--faint);
  border-bottom: 1.5px solid var(--faint);
  transform: rotate(45deg);
  transition: transform 0.15s;
}
.account-menu[open] .caret { transform: rotate(-135deg); margin-top: 3px; }

.account-menu .menu {
  position: absolute;
  right: 0;
  top: calc(100% + 8px);
  z-index: 30;
  display: flex;
  flex-direction: column;
  width: 250px;
  max-width: calc(100vw - 32px);
  background: var(--surface);
  border: 1px solid var(--border-strong);
  box-shadow: 4px 4px 0 var(--primary);
  font-family: var(--font-ui);
  font-size: 12.5px;
}

.menu-who {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 14px;
  border-bottom: 1px solid var(--border);
}
.menu-id { display: flex; flex-direction: column; gap: 3px; min-width: 0; }

.account-menu .menu > a {
  padding: 11px 14px;
  color: var(--text);
  text-decoration: none;
}
.account-menu .menu > a:hover { background: var(--rt-hover); }
.account-menu .menu > a[aria-current="page"] { box-shadow: inset 2px 0 0 var(--primary); }

.signout { margin: 0; border-top: 1px solid var(--border); }
.signout button {
  width: 100%;
  height: auto;
  padding: 11px 14px;
  border: 0;
  justify-content: flex-start;
  color: var(--muted);
  font-size: 12.5px;
  font-weight: 400;
}
.signout button:hover { background: var(--rt-hover); color: var(--rt-accent-ink); }

.profile-name {
  font-weight: 600;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.who {
  color: var(--muted);
  font-size: 11.5px;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* Narrower than the design's sidebar fits beside a page: the sidebar becomes a
   drawer from the left that the top bar's menu button opens. The drawer is a
   popover, so the browser opens and closes it and dismisses it on Escape or a
   tap outside; no script is needed, which matters because the CSP forbids it
   inline. The account controls move into the drawer's foot. */
@media (max-width: 960px) {
  .shell {
    grid-template-columns: minmax(0, 1fr);
    grid-template-rows: auto 1fr;
    grid-template-areas:
      "top"
      "main";
  }

  .sidebar:not(:popover-open) { display: none; }
  .sidebar {
    position: fixed;
    inset: 0 auto 0 0;
    width: min(260px, 86vw);
    height: 100dvh;
    transform: translateX(-100%);
    transition: transform 0.2s ease, overlay 0.2s allow-discrete, display 0.2s allow-discrete;
  }
  .sidebar:popover-open { transform: none; }
  @starting-style {
    .sidebar:popover-open { transform: translateX(-100%); }
  }
  .sidebar::backdrop { background: var(--rt-scrim); }

  .sidebar-head { justify-content: space-between; }
  .menu-toggle, .drawer-close { display: inline-flex; }

  .drawer-account {
    display: flex;
    flex-direction: column;
    flex: none;
    border-top: 1px solid var(--border);
    font-family: var(--font-ui);
  }
  .drawer-account .menu-who { padding: 14px 16px 10px; border-bottom: 0; flex-direction: column; align-items: flex-start; gap: 2px; }
  .drawer-account > a {
    padding: 10px 16px;
    color: var(--text);
    text-decoration: none;
  }
  .drawer-account > a:hover { background: var(--rt-hover); }
  /* The theme moves into the drawer, as a row beside Profile settings: the
     top bar has no room for it on a phone. */
  .topbar .theme-toggle { display: none; }
  .drawer-theme {
    display: flex;
    align-items: center;
    justify-content: flex-start;
    gap: 10px;
    width: 100%;
    height: auto;
    min-height: 0;
    padding: 10px 16px;
    border: 0;
    background: transparent;
    color: var(--text);
    text-align: left;
    box-shadow: none;
    transform: none;
    cursor: pointer;
    font: inherit;
  }
  .drawer-theme svg { flex: none; color: var(--muted); }
  .drawer-theme:hover { background: var(--rt-hover); box-shadow: none; transform: none; }
  .drawer-theme[hidden] { display: none; }

  .drawer-account > a.active { background: var(--rt-active); box-shadow: inset 2px 0 0 var(--primary); }

  .topbar { padding: 0 12px; }
  .topbar-brand { display: block; }
  .topbar-brand img { display: block; width: 28px; height: 28px; }
  .menu-toggle { margin-left: -4px; border: 0; }
}

@media (max-width: 720px) {
  .sending-label, .balance-label, .add-text { display: none; }
  .sending { padding: 0 4px; }
}

/* A phone: everything in the bar must fit beside a five-figure balance, so
   the status is its dot alone (its word kept for a screen reader) and the
   rest tightens. Nothing here may scroll sideways. */
@media (max-width: 480px) {
  /* The balance keeps its full width (it may not shrink under its own
     Top up button), and the account menu gives up its caret to make room:
     a five-figure balance still fits at 320px beside the initials. */
  .balance { flex: none; }
  .account-menu .caret { display: none; }
  .account-menu summary { padding: 0 5px; }
  .topbar { gap: 8px; }
  .sending svg { display: none; }
  .sending-state {
    position: absolute;
    width: 1px;
    height: 1px;
    overflow: hidden;
    clip: rect(0 0 0 0);
    white-space: nowrap;
  }
  .balance { min-width: 0; }
  .balance-link { padding: 0 8px; }
  .balance-value { font-size: 18px; white-space: nowrap; }
  .balance-add { padding: 0 8px; }
  .topbar .theme-toggle { width: 32px; min-width: 32px; }
}

@media (prefers-reduced-motion: reduce) {
  .sidebar { transition: none; }
  .sending .dot, .sending .dot::after { animation: none; }
}

/* ── Layout ──────────────────────────────────────────────────────────────── */

main {
  /* width rather than the auto a block would normally take. As a grid item an
     auto width resolves against the content, so one table wider than the phone
     stretches the whole column and drags the page sideways with it. Resolving
     against the track instead keeps main the width of the screen and leaves
     the overflow to the table's own scroller. */
  width: 100%;
  min-width: 0;
  /* One column width for every page, so moving between them never shifts
     the content's edges. */
  max-width: 1280px;
  margin: 0 auto;
  padding: 36px clamp(16px, 4vw, 40px) 80px;
}

/* The signed-out shell: no card and no chrome, a narrow column on the page's
   own ground with the brand red glowing in from the top. It follows the theme
   like everything else here. The operator console's signed-out pages are the
   same shell. */
body:has(main.centered) {
  background:
    radial-gradient(900px 420px at 50% -120px, rgb(237 70 45 / 22%), transparent 70%),
    var(--bg);
}

/* min-height rather than height, and centring only from a viewport tall enough
   to spare it: on a short screen a centred column is one whose top is
   scrolled off and unreachable. */
main.centered {
  display: flex;
  flex-direction: column;
  min-height: 100vh;
  min-height: 100dvh;
  max-width: 408px;
  padding: 48px 24px;
}

@media (min-height: 560px) {
  main.centered { justify-content: center; padding-bottom: 72px; }
}

/* The card's box steps aside, which lets the lockup, the heading and the line
   under it be ordered ahead of the page-level error the layout prints first:
   an error reads under the heading, beside the form it is about, rather than
   above the logo. */
main.centered > .card { display: contents; }
main.centered .login-brand { order: -3; justify-content: center; margin: 0 0 36px; }
main.centered h1 { order: -2; }
main.centered .intro { order: -1; }

.login-brand .logo { width: 176px; height: auto; }

/* ── Cards ───────────────────────────────────────────────────────────────── */

/* The console's panel: a filled slab with a hard offset shadow, square like
   everything else here. A page is a stack of them, each under its own
   heading. */
.card {
  padding: 20px 22px;
  margin-bottom: 24px;
  background: var(--panel);
  border: 1px solid var(--border);
}

.card > h2:first-child, .card > h1:first-child { margin-top: 0; }

/* Spacing that the markup would otherwise carry as a style attribute, which the
   CSP drops on the floor. A paragraph or a run of controls after the facts of a
   card needs air; the last thing in a card does not need a trailing margin. */
.card > p:last-child, .card > .facts + p:last-child { margin-bottom: 0; }
.card .facts + p, .card > .notice, .card > .banner { margin-top: 16px; }
.card > .notice:first-child, .card > .banner:first-child { margin-top: 0; }
.card > .row-actions { margin-top: 20px; }

/* Signed out: a heading a size down from a page's, centred like everything
   above the form, and the paragraphs around the form centred with it. A
   warning banner keeps its box and its alignment. */
main.centered h1 { margin: 0 0 18px; font-size: 30px; letter-spacing: 0.01em; text-align: center; }
main.centered .card > p:not(.banner) { text-align: center; }
main.centered .intro { margin: -8px 0 18px; color: var(--muted); font-size: 12px; }
main.centered .card > p a { color: var(--muted); }
main.centered .card > p a:hover { color: var(--text); }

/* The one action on a signed-out page: the column's width, a taller box to
   press, and the brand red rather than the console's cream slab. */
main.centered form button {
  width: 100%;
  min-height: var(--control-h);
  margin-top: 22px;
  font-size: 14px;
}
main.centered button.primary { background: var(--primary); color: #fff; box-shadow: var(--press); }
main.centered button.primary:hover { color: #fff; }

/* ── Sign-in ─────────────────────────────────────────────────────────────── */

/* The sign-in page: the form on the page's ground in the right half, and the
   brand panel fixed down the left. Fixed rather than a second column, so the
   form column keeps the signed-out layout above — and with it the ordering
   that puts the page's error under the heading. Below 900px the panel goes
   and the form is the page. */
main.centered:has(.brand-panel) { position: relative; max-width: 444px; }
main.centered:has(.brand-panel) h1 { margin-bottom: 10px; font-size: 40px; text-align: left; }
main.centered:has(.brand-panel) .card > p.intro { margin: 0 0 22px; font-size: 13.5px; text-align: left; }
/* "Check your email" has no form under it, so its words are the whole page. */
main.centered:has(.brand-panel) .card.sent > p.intro { font-size: 16px; line-height: 1.6; color: var(--text); }
main.centered:has(.brand-panel) .card.sent > p.sent-note { margin: 0 0 22px; font-size: 14px; line-height: 1.6; text-align: left; }
main.centered:has(.brand-panel) .card > p.apply {
  margin: 24px 0 0;
  padding-top: 18px;
  border-top: 1px solid var(--border);
  color: var(--muted);
  text-align: left;
}
main.centered:has(.brand-panel) .card > p.apply a { color: var(--rt-accent-ink); }
main.centered:has(.brand-panel) .card > p.apply a:hover { color: var(--primary); }

main.centered:has(.brand-panel) input[type="email"],
main.centered:has(.brand-panel) input[type="password"],
main.centered:has(.brand-panel) input[type="text"] {
  margin-bottom: 22px;
  background: var(--surface);
  font-size: 14px;
}
main.centered:has(.brand-panel) .pw-field { margin-bottom: 22px; }
main.centered:has(.brand-panel) .pw-field > input { margin-bottom: 0; }
main.centered:has(.brand-panel) label.acknowledge { margin-top: 0; font-size: 13px; }

.label-row { display: flex; justify-content: space-between; align-items: baseline; gap: 12px; }
.label-row a { margin-bottom: 8px; color: var(--rt-accent-ink); font-size: 12.5px; text-decoration: none; }
.label-row a:hover { color: var(--primary); }

main.centered:has(.brand-panel) form button.primary { box-shadow: 4px 4px 0 rgb(240 74 46 / 35%); }
main.centered:has(.brand-panel) form button.primary:hover {
  transform: translate(-2px, -2px);
  box-shadow: 6px 6px 0 rgb(240 74 46 / 35%);
}

main.centered:has(.brand-panel) .theme-toggle { position: absolute; top: 32px; right: clamp(20px, 5vw, 64px); }

.brand-panel { display: none; }
body:has(main.centered .brand-panel) { background: var(--bg); }

@media (min-width: 900px) {
  main.centered:has(.brand-panel) {
    width: 50%;
    max-width: none;
    margin: 0 0 0 50%;
    padding: 96px clamp(20px, 5vw, 64px) 48px;
    align-items: center;
  }
  main.centered:has(.brand-panel) > :not(.brand-panel),
  main.centered:has(.brand-panel) > .card > :not(.theme-toggle) { width: 100%; max-width: 380px; }
  main.centered:has(.brand-panel) .login-brand { display: none; }

  .brand-panel {
    position: fixed;
    inset: 0 50% 0 0;
    display: flex;
    flex-direction: column;
    overflow: hidden;
    background: #efeae0;
    color: #111;
    font-family: var(--font-ui);
  }
}

/* The panel is the site's cream page whatever the console's theme, so it
   names its colours outright rather than through the tokens. */
.bp-logo { padding: 32px clamp(24px, 5vw, 56px) 0; }
.bp-logo img { display: block; width: auto; height: 28px; }

.bp-head {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-end;
  gap: 14px 20px;
  padding: 40px clamp(24px, 5vw, 56px) 0;
}

.bp-title {
  margin: 0;
  font-family: var(--font-heading);
  font-size: clamp(48px, 5.6vw, 80px);
  font-weight: 700;
  line-height: 0.92;
  text-transform: uppercase;
}

/* The split-flap word: a red tile per letter, each turning over a beat after
   the one before. */
.bp-flap { display: flex; gap: 5px; perspective: 600px; }
.bp-flap span {
  position: relative;
  display: flex;
  align-items: center;
  justify-content: center;
  width: clamp(40px, 4.4vw, 62px);
  height: clamp(54px, 6vw, 84px);
  background: #f04a2e;
  color: #fff;
  font-family: var(--font-heading);
  font-size: clamp(40px, 4.8vw, 68px);
  font-weight: 700;
  line-height: 1;
  box-shadow: 0 3px 0 #111;
  animation: rt-flap 0.5s cubic-bezier(0.3, 1.3, 0.5, 1) both;
}
.bp-flap span::after {
  content: "";
  position: absolute;
  inset: 50% 0 auto;
  height: 2px;
  background: rgb(0 0 0 / 28%);
}
.bp-flap span:nth-child(2) { animation-delay: 0.06s; }
.bp-flap span:nth-child(3) { animation-delay: 0.12s; }
.bp-flap span:nth-child(4) { animation-delay: 0.18s; }
.bp-flap span:nth-child(5) { animation-delay: 0.24s; }
.bp-flap span:nth-child(6) { animation-delay: 0.3s; }
.bp-flap span.blank { background: transparent; border: 2px dashed #cfc8ba; box-shadow: none; }
.bp-flap span.blank::after { display: none; }

/* The device: a request going out along the top, the texts it sent below,
   and three running totals along the bottom. */
.bp-device {
  display: flex;
  flex: 1;
  flex-direction: column;
  min-height: 0;
  margin: 32px clamp(24px, 5vw, 56px) 36px;
  background: #f8f5ee;
  border: 2px solid #111;
  box-shadow: 8px 8px 0 #111;
}

.bp-console {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  padding: 12px 16px;
  background: #111;
  color: #efeae0;
  font-family: var(--font-label);
  font-size: 12px;
}
.bp-line { display: flex; align-items: center; gap: 8px; min-width: 0; }
.bp-prompt { color: #f04a2e; }
.bp-cmd {
  display: inline-block;
  overflow: hidden;
  white-space: nowrap;
  animation: rt-type 0.9s steps(17) both;
}
.bp-caret { flex: none; width: 7px; height: 14px; background: #efeae0; animation: rt-caret 1s steps(1) infinite; }
.bp-ok {
  flex: none;
  padding: 3px 8px;
  background: #f04a2e;
  color: #fff;
  font-weight: 600;
  letter-spacing: 0.06em;
  animation: rt-ok 0.3s 1s both;
}

.bp-feed {
  position: relative;
  flex: 1;
  background-image: linear-gradient(#e6e0d3 1px, transparent 1px);
  background-size: 100% 28px;
}
.bp-stack {
  position: absolute;
  inset: 0;
  display: flex;
  flex-direction: column;
  justify-content: flex-end;
  gap: 14px;
  padding: 18px 22px 22px;
  overflow: hidden;
  -webkit-mask-image: linear-gradient(180deg, transparent 0, #000 30%);
  mask-image: linear-gradient(180deg, transparent 0, #000 30%);
}

.bp-msg {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  align-self: flex-start;
  gap: 6px;
  max-width: 82%;
}
.bp-msg.right { align-items: flex-end; align-self: flex-end; }
.bp-msg.enter { animation: rt-bubble 0.5s 0.9s cubic-bezier(0.2, 1.2, 0.4, 1) both; }

.bp-to, .bp-status { font-family: var(--font-label); font-size: 10.5px; letter-spacing: 0.06em; }
.bp-to { color: #7a756c; }

.bp-bubble {
  padding: 12px 16px;
  background: #fff;
  color: #111;
  border: 2px solid #111;
  border-radius: 14px 14px 14px 0;
  box-shadow: 4px 4px 0 #111;
  font-size: 14px;
  line-height: 1.45;
  transition: background 0.4s, color 0.4s;
}
.bp-msg.right .bp-bubble { border-radius: 14px 14px 0 14px; }
.bp-msg.latest .bp-bubble { background: #111; color: #fff; box-shadow: 4px 4px 0 #f04a2e; }

.bp-status { display: flex; gap: 2px; color: #55514a; }
.bp-status.delivered { align-items: center; gap: 6px; color: #f04a2e; font-weight: 600; }
.bp-tick {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 14px;
  height: 14px;
  background: #f04a2e;
  color: #fff;
  font-size: 10px;
}
.bp-tick.fresh { animation: rt-tick 0.4s cubic-bezier(0.3, 1.5, 0.5, 1); }
.bp-dots span { animation: rt-dots 1s infinite; }
.bp-dots span:nth-child(2) { animation-delay: 0.18s; }
.bp-dots span:nth-child(3) { animation-delay: 0.36s; }

.bp-stats {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  border-top: 2px solid #111;
  color: #55514a;
  font-family: var(--font-label);
  font-size: 10.5px;
  letter-spacing: 0.1em;
  text-transform: uppercase;
}
.bp-stats > div { display: flex; flex-direction: column; gap: 4px; padding: 12px 16px; }
.bp-stats > div + div { border-left: 2px solid #111; }
.bp-stats b {
  color: #111;
  font-family: var(--font-heading);
  font-size: 26px;
  font-weight: 700;
  letter-spacing: 0;
  line-height: 1;
}
.bp-stats b.hot { color: #f04a2e; }

.bp-marquee {
  overflow: hidden;
  padding: 12px 0;
  background: #111;
  color: #efeae0;
  font-family: var(--font-heading);
  font-size: 20px;
  font-weight: 700;
  letter-spacing: 0.04em;
  text-transform: uppercase;
}
.bp-track { display: flex; width: max-content; animation: rt-marq 30s linear infinite; }
.bp-run { display: flex; flex: none; gap: 28px; padding-right: 28px; white-space: nowrap; }
.bp-run i { color: #f04a2e; font-style: normal; }

@keyframes rt-flap { 0% { transform: rotateX(90deg); } 60% { transform: rotateX(-12deg); } 100% { transform: rotateX(0); } }
@keyframes rt-bubble { 0% { transform: translateY(30px) scale(0.9); opacity: 0; } 100% { transform: none; opacity: 1; } }
@keyframes rt-marq { to { transform: translateX(-50%); } }
@keyframes rt-dots { 0%, 100% { opacity: 0.25; } 50% { opacity: 1; } }
@keyframes rt-type { from { max-width: 0; } to { max-width: 17ch; } }
@keyframes rt-caret { 50% { opacity: 0; } }
@keyframes rt-ok { 0% { opacity: 0; transform: translateX(-6px); } 100% { opacity: 1; transform: none; } }
@keyframes rt-tick { 0% { transform: scale(0); } 70% { transform: scale(1.3); } 100% { transform: scale(1); } }

@media (prefers-reduced-motion: reduce) {
  .brand-panel *, .brand-panel *::after { animation: none !important; }
}

/* ── Messages ────────────────────────────────────────────────────────────── */

/* Tinted, with the loud left edge the console uses: at a glance the colour says
   which of the three this is, and the weight of the edge says it is about the
   page rather than part of it. */
.error, .notice, .banner {
  padding: 12px 16px;
  margin: 0 0 20px;
  border: 1px solid;
  border-left-width: 3px;
  font-size: 12.5px;
}

.error { background: var(--rt-alert-bg); border-color: var(--rt-alert-line); border-left-color: var(--primary); color: var(--text); }
.notice { background: var(--ok-bg); border-color: transparent; border-left-color: var(--ok-text); color: var(--text); }
.banner { background: var(--warn-bg); border-color: transparent; border-left-color: var(--warn-text); color: var(--text); }
.banner strong, .notice strong, .error strong { display: block; margin-bottom: 4px; }
/* Some errors are Go error strings, lowercase by convention; a sentence on the page starts with a capital. */
.error::first-letter { text-transform: uppercase; }

/* An operator signed in as the customer. The words on the left, the way out on
   the right, and the way out drops under the words when there is no room. */
.banner.impersonation {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 10px 16px;
}
.banner.impersonation p { margin: 0; flex: 1 1 32ch; }
.impersonation-end { margin: 0; }


/* A credential shown exactly once. It is loud on purpose: the page that renders
   it is the only place it will ever exist, and somebody who scrolls past it has
   lost it. The red edge is the same marker the console puts on a message body
   it wants read. */
.card.secret-once {
  border-left: 4px solid var(--primary);
}

code.secret {
  display: block;
  margin-top: 12px;
  padding: 14px 16px;
  background: var(--input);
  border: 1px solid var(--border-strong);
  color: var(--text);
  font-size: 13px;
  /* A key and a signing secret are long, unbroken and case-sensitive. Wrapping
     anywhere beats a horizontal scrollbar over the one string on the page that
     has to be copied whole. */
  word-break: break-all;
  user-select: all;
}

/* ── Forms ───────────────────────────────────────────────────────────────── */

/* Field names, in the console's key: small, tracked out, uppercase, muted. The
   field itself carries the gap below, so the label only owns the gap above. */
label {
  display: block;
  margin-bottom: 8px;
  font-family: var(--font-label);
  font-size: 11px;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--muted);
}

input, select, button {
  font: inherit;
  padding: 10px 12px;
  border: 1px solid var(--border-strong);
  border-radius: 0;
  background: var(--input);
  color: var(--text);
}

input, select { font-size: 13.5px; }
:where(input:not([type="checkbox"]):not([type="radio"]):not([type="hidden"]), select) {
  height: var(--control-h);
  padding-top: 0;
  padding-bottom: 0;
}

input[type="email"], input[type="password"], input[type="text"],
input[type="number"], select {
  width: 100%;
  margin-bottom: 16px;
}

/* A password box with the show/hide eye portal.js adds inside its right edge.
   The wrapper takes the box's bottom margin so the button, pinned top to
   bottom, spans the input alone. */
.pw-field {
  position: relative;
  display: block;
  margin-bottom: 16px;
}

.pw-field > input { margin-bottom: 0; padding-right: 42px; }

/* Edge draws its own reveal eye; two would be one too many. */
.pw-field > input::-ms-reveal { display: none; }

.pw-field > .pw-toggle {
  position: absolute;
  /* A card's button rule aligns it to flex-start, which an absolute box
     obeys too and so stops stretching between top and bottom. */
  align-self: stretch;
  top: 1px;
  right: 1px;
  bottom: 1px;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 38px;
  padding: 0;
  margin: 0;
  border: 0;
  background: transparent;
  color: var(--muted);
  box-shadow: none;
  transform: none;
}

.pw-field > .pw-toggle:hover { color: var(--text); }
.pw-field > .pw-toggle:focus-visible { outline-offset: -2px; }

.fields .pw-field { margin-bottom: 0; }

/* Short fields and the button that submits them on one line, the way the
   operator console lays out its own profile: three password boxes are three
   dozen characters between them, and stacking them full width makes a form
   that is mostly empty box.

   Each field is elastic down to a readable minimum and wraps onto its own line
   below that, rather than shrinking into something nothing fits in. Bottoms
   align, so the button sits on the same line as the inputs even though its
   smaller type makes it the shorter box. */
.fields {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-end;
  gap: 12px;
  margin-top: 16px;
}

/* A column rather than a wrapping <label>: an input inherits its font, and
   inside the label it would inherit the caption's 10px. */
.fields .field {
  display: flex;
  flex-direction: column;
  flex: 1 1 190px;
  min-width: 0;
}

.fields .field label { margin-bottom: 6px; }
.fields .field input { margin-bottom: 0; }
/* Grows with the fields, so a line is always filled — including the one the
   button lands on alone once the row wraps. */
.fields button { flex: 1 0 auto; }

/* A native select sizes itself, ignoring the padding and line-height that set
   an input's height, so the two never line up. Without native appearance it
   takes the same box as an input. The arrow is two gradient triangles: the
   CSP allows no data: image to draw it with. */
select {
  appearance: none;
  padding-right: 32px;
  background-image:
    linear-gradient(45deg, transparent 50%, var(--muted) 50%),
    linear-gradient(135deg, var(--muted) 50%, transparent 50%);
  background-position: calc(100% - 17px) 55%, calc(100% - 12px) 55%;
  background-size: 5px 5px;
  background-repeat: no-repeat;
  cursor: pointer;
}
select:hover { background-image:
    linear-gradient(45deg, transparent 50%, var(--text) 50%),
    linear-gradient(135deg, var(--text) 50%, transparent 50%); }

input::placeholder { color: var(--faint); }

input:hover, select:hover { border-color: var(--faint); }

input[type="checkbox"], input[type="radio"] { accent-color: var(--primary); }

input:focus-visible, select:focus-visible {
  outline: none;
  border-color: var(--primary);
  box-shadow: 0 0 0 1px var(--primary);
}

/* A select marked data-searchable, as portal.js turns it: a text box dressed as
   the select it replaces — same arrow — over a list that narrows as you type.
   The list floats over whatever is below rather than pushing it down. */
.combo {
  position: relative;
  display: block;
  min-width: 0;
}

.combo > .combo-input {
  width: 100%;
  margin: 0;
  padding-right: 32px;
  text-overflow: ellipsis;
  background-image:
    linear-gradient(45deg, transparent 50%, var(--muted) 50%),
    linear-gradient(135deg, var(--muted) 50%, transparent 50%);
  background-position: calc(100% - 17px) 55%, calc(100% - 12px) 55%;
  background-size: 5px 5px;
  background-repeat: no-repeat;
}

.combo-list {
  position: absolute;
  z-index: 20;
  top: calc(100% + 4px);
  left: 0;
  min-width: 100%;
  max-width: min(420px, calc(100vw - 32px));
  max-height: 280px;
  overflow-y: auto;
  margin: 0;
  padding: 4px 0;
  list-style: none;
  background: var(--surface);
  border: 1px solid var(--border-strong);
  box-shadow: var(--shadow);
}

.combo-list > li {
  padding: 7px 12px;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  cursor: pointer;
}

.combo-list > li[hidden] { display: none; }
.combo-list > li[aria-selected="true"] { color: var(--primary); }
.combo-list > li.active { background: var(--info-bg); color: var(--text); }
.combo-list > li.combo-empty { color: var(--muted); cursor: default; }

button:focus-visible, a:focus-visible, .plans:focus-visible {
  outline: 2px solid var(--primary);
  outline-offset: 2px;
}

/* A checkbox and its words on one line, which the block-level label rule above
   would otherwise stack. The label is a sentence here rather than a field name,
   so it drops the uppercase treatment with it. */
label.check {
  display: flex;
  align-items: baseline;
  gap: 8px;
  margin-bottom: 8px;
  font-family: var(--font-ui);
  font-size: 12.5px;
  font-weight: 400;
  letter-spacing: normal;
  text-transform: none;
  color: var(--text);
}
label.check input { margin: 0; flex: none; }

/* The policy agreement on the sign-in form: a sentence rather than a field, so
   it is quieter than the fields above it, and the three policies in it stand
   out as the links they are. Top-aligned, since it wraps. */
label.acknowledge {
  align-items: flex-start;
  margin: 18px 0 0;
  line-height: 1.6;
  color: var(--muted);
}
label.acknowledge input { margin-top: 3px; }
label.acknowledge a {
  color: var(--text);
  font-weight: 500;
  text-decoration: underline;
  text-underline-offset: 2px;
}

fieldset {
  border: 1px solid var(--border);
  padding: 14px 16px;
  margin: 0 0 16px;
}

legend {
  padding: 0 6px;
  font-family: var(--font-label);
  font-size: 11px;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--muted);
}

/* ── Buttons ─────────────────────────────────────────────────────────────── */

/* Three weights, the console's, and each says what it is for.
 *
 * The plain button is the quiet one: an outline, no fill, for the action that
 * is a way out or a refinement — Cancel, Close, Filter, Send test. The console
 * dresses sign-out and its theme control the same way.
 *
 * .primary is the site's call-to-action: a solid slab with a hard offset shadow
 * that the button presses into on hover. It is cream rather than red so the
 * brand red stays free to mean "this one is destructive", which is .danger. */
button {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  padding: 0 14px;
  min-height: var(--control-h);
  background: transparent;
  color: var(--text);
  border-color: var(--border-strong);
  font-size: 13px;
  font-weight: 500;
  cursor: pointer;
  transition: transform 0.12s ease, box-shadow 0.12s ease, background-color 0.12s ease, border-color 0.12s ease;
}

button:hover { background: var(--rt-hover); border-color: var(--faint); }

button.primary {
  background: var(--primary);
  color: #fff;
  border-color: var(--primary);
  box-shadow: var(--press);
}

button.primary:hover {
  background: var(--primary);
  border-color: var(--primary);
  transform: translate(-1px, -1px);
  box-shadow: 4px 4px 0 rgb(240 74 46 / 35%);
}
button.primary:active { transform: translate(2px, 2px); box-shadow: none; }

/* Destructive: the accent in the type and the outline, not the fill — the
   fill is the primary action's, and Revoke must not look like it. */
button.danger {
  color: var(--rt-accent-ink);
  border-color: var(--border-strong);
}

button.danger:hover {
  background: var(--rt-accent-bg);
  border-color: var(--rt-accent-ink);
}

/* A button that sits in a table row or a dialog's head rather than at the foot
   of a form. Same button, smaller: a column of full-size ones shouts down the
   data beside it. */
button.small { min-height: var(--control-h-sm); padding: 0 10px; font-size: 12px; }

/* A button mid-submit. portal.js swaps its label for the busy one and holds its
   width, so the row does not jump; here it is left looking pressed — the slab
   down against its shadow — rather than faded, which reads as "not allowed"
   instead of "working on it". */
button[aria-busy="true"] {
  cursor: progress;
  pointer-events: none;
}

button.primary[aria-busy="true"] { transform: translate(2px, 2px); box-shadow: none; }
.signout button[aria-busy="true"] { color: var(--muted); }

/* The theme control. Quiet, because it is a preference rather than an action —
   it sits beside the balance and must not compete with it. A square holding
   the glyph alone, the same height as its neighbours in the top bar. */
.theme-toggle, .menu-toggle, .drawer-close {
  align-items: center;
  justify-content: center;
  flex: none;
  width: var(--topbar-control);
  height: var(--topbar-control);
  min-height: 0;
  padding: 0;
  border-color: var(--border);
  color: var(--muted);
  font-size: 15px;
  line-height: 1;
}
.theme-toggle:hover, .menu-toggle:hover, .drawer-close:hover { color: var(--text); }
.theme-toggle { display: inline-flex; }
.theme-toggle[hidden] { display: none; }
/* A live filter applies itself, so its Filter button goes; buttons are
   display: inline-flex, which would otherwise win over [hidden]. */
[data-filter-submit][hidden] { display: none; }
main[aria-busy="true"] .panel.log, main[aria-busy="true"] .an-grid, main[aria-busy="true"] .kpis { opacity: 0.6; transition: opacity 0.15s; }
.drawer-close { font-size: 14px; }

/* A link that does what a quiet button would — go to another page — dressed as
   one, so an action does not read as a sentence. Navigation stays a link: it
   works without script, opens in a new tab, and says where it goes. */
a.button {
  display: inline-flex;
  align-items: center;
  min-height: var(--control-h);
  padding: 0 14px;
  border: 1px solid var(--border-strong);
  color: var(--text);
  font-weight: 500;
  font-size: 13px;
  text-decoration: none;
  white-space: nowrap;
}
a.button:hover { background: var(--rt-hover); border-color: var(--faint); }

/* Dressed as the quiet button it sits beside, border and all: it is the other
   way to act on the filter row, and bare text there read as a caption. */
.clear {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  box-sizing: border-box;
  min-height: var(--control-h);
  padding: 0 12px;
  border: 1px solid var(--border-strong);
  color: var(--muted);
  text-decoration: none;
  font-size: 13px;
  font-weight: 500;
}

.clear:hover { color: var(--text); background: var(--rt-hover); }

/* ── Figures ─────────────────────────────────────────────────────────────── */

/* The console's stat row: tiles sharing their borders like the site's feature
   grid rather than floating as separate cards, on the page ground rather than
   inside a panel. */
.figures {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(170px, 1fr));
  gap: 0;
  margin-bottom: 24px;
  background: var(--panel);
  border: 1px solid var(--border);
}

.figure {
  padding: 20px 22px;
  border-right: 1px solid var(--border);
  border-bottom: 1px solid var(--border);
  margin: 0 -1px -1px 0;
}


.figure .label {
  margin-bottom: 8px;
  color: var(--muted);
  font-family: var(--font-label);
  font-size: 10.5px;
  letter-spacing: 0.14em;
  text-transform: uppercase;
}

.figure .value {
  font-family: var(--font-heading);
  font-size: 34px;
  font-weight: 600;
  line-height: 0.95;
  font-variant-numeric: tabular-nums;
  overflow-wrap: anywhere;
}

.figure .value.low { color: var(--rt-accent-ink); }

/* The unit beside a figure — "/s", "segments". Small and muted so the number
   stays the thing being read, rather than competing with its own label. */
.figure .value .unit {
  font-family: var(--font-mono);
  font-size: 13px;
  font-weight: 400;
  color: var(--muted);
}

/* An offer under a figure — "Top up" under the balance. */
.figure p { margin: 8px 0 0; }

/* ── Facts ───────────────────────────────────────────────────────────────── */

/* Label and value in one row, ruled off from the next. Read down the labels or
   down the values; either way the pair is on one line, which a two-column grid
   only manages until one value runs long. */
dl.facts { margin: 0; }
dl.facts div {
  display: flex;
  justify-content: space-between;
  gap: 16px;
  padding: 8px 0;
  border-bottom: 1px solid var(--border);
}
dl.facts div:last-child { border-bottom: none; }
dl.facts dt { color: var(--muted); }
dl.facts dd { margin: 0; text-align: right; overflow-wrap: anywhere; }

/* ── Tables ──────────────────────────────────────────────────────────────── */

/* Inside a card, so the fill is the card's and the header row is the step up
   from it — the same surface-then-surface-2 nesting the console's dialogs use.
   A table on the page ground would want the fill itself. */
table {
  width: 100%;
  border-collapse: collapse;
  border: 1px solid var(--border);
  font-size: 13px;
}

th, td {
  padding: 12px 14px;
  text-align: left;
  border-bottom: 1px solid var(--border);
}

th {
  padding-block: 10px;
  background: var(--surface-2);
  font-size: 12px;
  font-weight: 500;
  color: var(--muted);
}

tbody tr:last-child td { border-bottom: none; }
tbody tr:hover { background: var(--rt-hover); }

td.num, th.num { text-align: right; font-variant-numeric: tabular-nums; }

td a { color: var(--text); text-decoration: none; }
td a:hover { color: var(--rt-accent-ink); }

.table-wrap { overflow-x: auto; }

/* A table whose cells hold URLs and payload metadata rather than short words.
 *
 * Without the floor, a narrow viewport is resolved by squeezing the columns
 * instead of scrolling: .wrap then breaks a URL at every character and the
 * column ends up one glyph wide, which is unreadable in a way a sideways
 * scrollbar is not. The width is chosen to be just past a phone, so it only
 * ever engages where the alternative is that collapse. */
.table-wrap table.wide { min-width: 700px; }
.details table.wide { min-width: 520px; }

/* The attempt the row was opened from, among its siblings. */
tr.current td { background: var(--info-bg); }

/* ── Pills ───────────────────────────────────────────────────────────────── */

/* The site's small tag: square, mono, uppercase, tracked out, outlined in
   whatever colour its state carries. */
.pill {
  display: inline-block;
  padding: 1px 7px;
  font-size: 11.5px;
  font-weight: 500;
  line-height: 1.6;
  white-space: nowrap;
  color: var(--muted);
  background: var(--rt-active);
}
.pill::before {
  content: "";
  display: inline-block;
  width: 5px;
  height: 5px;
  margin: 0 6px 1px 0;
  border-radius: 50%;
  background: currentcolor;
  vertical-align: middle;
}

.pill.ok { background: var(--ok-bg); color: var(--ok-text); }
.pill.warn { background: var(--warn-bg); color: var(--warn-text); }
.pill.bad { background: var(--rt-accent-bg); color: var(--rt-accent-ink); }

/* ── Rows, filters and pager ─────────────────────────────────────────────── */

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

td .row-actions { gap: 8px; justify-content: flex-end; }
td .row-actions form { margin: 0; }
/* The cell is sized by its buttons rather than by the longest word in them, so
   a narrow column does not hyphenate "Send test" down the middle. */
td .row-actions button { white-space: nowrap; }

/* A flex grid: every control grows, so each line the row wraps onto is filled
   edge to edge and there is never a strip of empty space after the last one.
   Stretched, so the button and the Clear link are the same height as the
   fields beside them.

   Each control's width comes from its basis, not its content — min-width: 0
   is what lets it — or the endpoint picker sizes itself to its longest URL
   and takes the whole row with it. The option text is truncated by the
   browser rather than by us, so the full URL is still there when the menu is
   open. The visually hidden labels are absolutely positioned and the hidden
   inputs not displayed, so neither takes a cell — and the labels are left out
   of these rules, whose width would otherwise undo the 1px that hides them. */
.filters {
  display: flex;
  flex-wrap: wrap;
  align-items: stretch;
  gap: 8px;
  margin-bottom: 20px;
}
.filters > :not(.visually-hidden) { flex: 1 1 180px; min-width: 0; margin-bottom: 0; }
.filters > button, .filters > .clear { flex: 1 0 90px; }
.filters select { width: auto; margin-bottom: 0; }
.filters label.check {
  align-items: center;
  margin-bottom: 0;
  padding: 0 12px;
  border: 1px solid var(--border-strong);
}

.pager {
  display: flex;
  align-items: center;
  gap: 24px;
  margin-top: 20px;
  color: var(--faint);
  font-size: 12px;
}

.pager a { color: var(--text); text-decoration: none; border-bottom: 1px solid var(--primary); }
.pager a:hover { color: var(--rt-accent-ink); }

/* The sent / received switch above the message log: two links, since the CSP
   forbids the script a client-side tab would need. */
.view-tabs {
  display: flex;
  gap: 20px;
  margin-bottom: 20px;
  border-bottom: 1px solid var(--border);
  font-size: 13px;
}

.view-tabs a { margin-bottom: -1px; padding: 0 0 10px; color: var(--muted); text-decoration: none; border-bottom: 2px solid transparent; }
.view-tabs a:hover { color: var(--text); }
.view-tabs a.active { color: var(--text); font-weight: 500; border-bottom-color: var(--primary); }

/* The analytics page's filters: labelled fields rather than the log's
   placeholder-only row, because a date field has no placeholder to name it. */
.analytics-filters { margin: 0; }
.analytics-filters .field { flex-basis: 150px; }
/* Room for a key's name and prefix, which the picker otherwise clips. */
.analytics-filters .field.wide { flex-basis: 240px; }
.analytics-filters .field select { width: 100%; margin-bottom: 0; }
/* Every control in the row one height, sitting on the same line: the
   inputs, the picker, Apply and Reset. */
.analytics-filters > button,
.analytics-filters > .clear { flex: 0 0 auto; padding: 0 18px; align-self: flex-end; }
.analytics-filters > .clear { margin: 0; }

/* One tile per message status on the analytics page: the figure row's
   shared-border grid, wider, each tile ruled across the top in its status's
   colour so the row reads as delivered-to-failed at a glance. */
/* The five totals above them, narrow enough to share one row on a laptop. */
.figures.totals { grid-template-columns: repeat(auto-fit, minmax(130px, 1fr)); }

.status-tiles { grid-template-columns: repeat(auto-fit, minmax(240px, 1fr)); }
.status-tiles .figure,
.status-tiles .figure:first-child { box-shadow: inset 0 3px 0 var(--border-strong); }
.status-tiles .figure.ok { box-shadow: inset 0 3px 0 var(--ok-text); }
.status-tiles .figure.warn { box-shadow: inset 0 3px 0 var(--warn-text); }
.status-tiles .figure.bad { box-shadow: inset 0 3px 0 var(--danger); }

.status-tiles .tile-head {
  display: flex;
  justify-content: space-between;
  align-items: center;
  margin-bottom: 14px;
}
.status-tiles dl.facts { margin-top: 12px; font-size: 12px; }
.status-tiles dl.facts div { padding: 6px 0; }

/* The status's share of all messages. A progress element because the CSP
   refuses the style attribute a sized div would need. */
.status-tiles progress {
  display: block;
  width: 100%;
  height: 4px;
  margin-top: 14px;
  border: none;
  appearance: none;
  background: var(--border);
  color: var(--muted);
}
.status-tiles progress::-webkit-progress-bar { background: var(--border); }
.status-tiles progress::-webkit-progress-value { background: var(--muted); }
.status-tiles progress::-moz-progress-bar { background: var(--muted); }
.status-tiles .ok progress::-webkit-progress-value { background: var(--ok-text); }
.status-tiles .ok progress::-moz-progress-bar { background: var(--ok-text); }
.status-tiles .warn progress::-webkit-progress-value { background: var(--warn-text); }
.status-tiles .warn progress::-moz-progress-bar { background: var(--warn-text); }
.status-tiles .bad progress::-webkit-progress-value { background: var(--danger); }
.status-tiles .bad progress::-moz-progress-bar { background: var(--danger); }

/* ── Plans ───────────────────────────────────────────────────────────────── */

/* One row, scrolled sideways, rather than a grid that wraps.
 *
 * A wrapping grid put the cheapest three plans on one line and the most
 * expensive on its own below, which reads as a separate thing rather than the
 * top of a ladder. A single row keeps the price order intact and stays intact
 * as plans are added — the catalogue is operator-editable, so "how many fit"
 * is not something this stylesheet gets to assume.
 *
 * The cards do not shrink. Squeezing four plans into a phone's width is how
 * you get four unreadable columns; scrolling past them is how every pricing
 * page on a phone already behaves. */
.plans {
  display: flex;
  gap: 16px;
  margin: 8px 0 24px;
  overflow-x: auto;
  /* Snap to the start of a card so a flick never leaves one half off-screen. */
  scroll-snap-type: x proximity;
  /* Room for the focus ring, which the scroll container would otherwise clip. */
  padding: 4px 4px 14px;
  margin-inline: -4px;
}

/* The scrollbar is the affordance. Hiding it is the common instinct and the
   wrong one: a horizontal scroller with no visible track is a row of cards that
   most people never discover the rest of. Styled down, not away. */
.plans::-webkit-scrollbar { height: 8px; }
.plans::-webkit-scrollbar-track { background: var(--surface-2); }
.plans::-webkit-scrollbar-thumb { background: var(--border-strong); }
.plans::-webkit-scrollbar-thumb:hover { background: var(--muted); }
.plans { scrollbar-width: thin; scrollbar-color: var(--border-strong) var(--surface-2); }

.plan {
  background: var(--panel);
  border: 1px solid var(--border);
  padding: 22px;
  display: flex;
  flex-direction: column;
  gap: 20px;
  /* Fixed width, never shrinking: this is what makes the row scroll instead of
     compressing. */
  flex: 0 0 300px;
  scroll-snap-align: start;
}

/* Auto margins centre the row while it fits and fall to zero once it
   overflows; justify-content: center would clip the first card instead. */
.plan:first-child { margin-left: auto; }
.plan:last-child { margin-right: auto; }

/* The plan being paid for, marked with the red edge the console puts on the
   thing you are looking at. */
.plan.current { border-color: var(--primary); box-shadow: inset 0 3px 0 var(--primary); }

.plan-head { display: flex; align-items: center; justify-content: space-between; gap: 12px; min-height: 22px; }
.plan-head h2 {
  margin: 0;
  font-family: var(--font-label);
  font-size: 13px;
  font-weight: 600;
  letter-spacing: 0.08em;
  text-transform: uppercase;
}

.plan .badge {
  flex: none;
  padding: 2px 8px;
  background: var(--primary);
  color: #fff;
  font-family: var(--font-label);
  font-size: 10.5px;
  font-weight: 600;
  letter-spacing: 0.06em;
  text-transform: uppercase;
}

.plan-price { display: flex; align-items: baseline; flex-wrap: wrap; gap: 4px 10px; }
.plan .amount {
  font-family: var(--font-heading);
  font-size: 56px;
  font-weight: 700;
  line-height: 0.9;
  font-variant-numeric: tabular-nums;
}
.plan .per {
  color: var(--muted);
  font-family: var(--font-label);
  font-size: 12px;
  letter-spacing: 0.04em;
  text-transform: uppercase;
}

.plan-rate {
  display: flex;
  flex-direction: column;
  gap: 6px;
  padding: 14px 0;
  border-block: 1px solid var(--border);
  color: var(--muted);
}
.plan-rate .side-label { font-size: 10.5px; color: var(--faint); }
.plan-rate .rate { color: var(--text); font-size: 22px; font-weight: 500; }
.plan-rate .track { fill: var(--border); }
.plan-rate .fill { fill: var(--muted); }
.plan.current .plan-rate .fill { fill: var(--primary); }

.plan > p { margin: 0; }

.plan ul {
  list-style: none;
  padding: 0;
  margin: 0;
  display: flex;
  flex-direction: column;
  gap: 10px;
  font-size: 13px;
}
.plan li { display: flex; gap: 10px; }
.plan li svg { flex: none; margin-top: 2px; }
.plan li .yes { color: var(--ok-text); }
.plan li code { color: inherit; }

/* Pinned to the bottom, so the buttons line up across cards whatever their
   descriptions run to. */
.plan-action {
  margin-top: auto;
  display: flex;
  flex-direction: column;
  gap: 8px;
}
.plan-action form { margin: 0; }
.plan-action button, .plan-action .button { width: 100%; justify-content: center; }
.plan-action p { margin: 0; line-height: 1.45; }


/* Ending the subscription: the accent in the type, not the fill, as the danger
   buttons are — it is a way out, not the action this page is selling. */
.plan .button.cancel { color: var(--rt-accent-ink); }

/* Add-ons bought with a paid plan: two small quantities above Subscribe. */
.plan-addons {
  display: grid;
  grid-template-columns: minmax(0, 1fr) 72px;
  align-items: center;
  gap: 8px 12px;
  margin-bottom: 12px;
  padding-top: 12px;
  border-top: 1px solid var(--border);
}
.plan-addons label { display: flex; flex-direction: column; font-size: 12.5px; line-height: 1.35; }
.plan-addons label .muted { font-size: 11.5px; }
.plan-addons input { width: 100%; min-height: var(--control-h-sm); height: var(--control-h-sm); text-align: right; font-family: var(--font-mono); }

/* The rest of the price list, under the cards. */
.price-list { display: flex; flex-wrap: wrap; gap: 20px; align-items: flex-start; }
.price-list > .panel { flex: 1 1 380px; min-width: 0; }
.price-list .panel-head p { margin: 6px 0 0; font-size: 12.5px; line-height: 1.55; }
.fee-list { list-style: none; margin: 0; padding: 0; }
.fee-list li { display: flex; align-items: center; gap: 14px; padding: 14px 22px; border-top: 1px solid var(--border); }
.fee-icon {
  flex: none;
  width: 30px;
  height: 30px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border: 1px solid var(--border-strong);
  color: var(--muted);
  font-family: var(--font-mono);
  font-size: 12px;
}

/* ── Add-ons ─────────────────────────────────────────────────────────────── */

.addon-counts { display: flex; flex-wrap: wrap; gap: 8px 28px; }
.addon-counts > span { display: inline-flex; align-items: baseline; gap: 8px; }

.allowance {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 6px 16px;
  margin: 0 0 16px;
  font-size: 13px;
}
.allowance .quiet-link { margin-left: auto; }

.addon-usage { display: flex; flex-wrap: wrap; gap: 16px; margin-bottom: 20px; }
.addon-usage > .panel { flex: 1 1 280px; display: flex; flex-direction: column; gap: 8px; }
.addon-figure { font-family: var(--font-heading); font-size: 34px; font-weight: 700; line-height: 1; text-transform: uppercase; }

.addon-form .panel-head p { margin: 6px 0 0; font-size: 12.5px; line-height: 1.55; max-width: 72ch; }
.addon-head { display: flex; align-items: center; justify-content: space-between; gap: 12px; }
.addon-tag {
  padding: 2px 8px;
  border: 1px solid var(--border-strong);
  color: var(--muted);
  font-family: var(--font-label);
  font-size: 10.5px;
  letter-spacing: 0.06em;
  text-transform: uppercase;
}
.addon-tag.ok { border-color: rgb(111 207 138 / 45%); color: var(--ok-text); }
td code.credit { color: var(--ok-text); }
.addon-usage .track { fill: var(--border); }
.addon-usage .fill { fill: var(--text); }

/* Side by side, the two cards end level: each fills the row's height and
   keeps its footer on the bottom edge. */
.addon-grid { display: flex; flex-wrap: wrap; align-items: stretch; gap: 16px; }
.addon-grid > .panel { flex: 1 1 380px; min-width: 0; margin: 0; display: flex; flex-direction: column; }
.addon-grid > .panel > form { display: flex; flex: 1; flex-direction: column; }
.addon-grid .addon-foot { margin-top: auto; }
.addon-grid .addon-foot { justify-content: space-between; align-items: center; background: var(--surface); }
.addon-grid .addon-foot > :last-child { margin-left: auto; }

/* − and + around the quantity, one bordered control. */
.stepper { display: inline-flex; align-items: stretch; height: 44px; border: 1px solid var(--border-strong); }
.stepper button { width: 44px; min-height: 0; height: auto; padding: 0; border: 0; background: transparent; color: var(--text); font-size: 18px; }
.stepper button:first-child { border-right: 1px solid var(--border-strong); }
.stepper button:last-child { border-left: 1px solid var(--border-strong); }
.stepper button:hover:not(:disabled) { background: var(--rt-hover); }
.stepper button:disabled { opacity: 0.4; cursor: default; }
.stepper input,
.addon-row .stepper input {
  align-self: stretch;
  width: 72px;
  min-height: 0;
  height: auto;
  margin: 0;
  padding: 0;
  border: 0;
  background: var(--bg);
  text-align: center;
  font-family: var(--font-mono);
  font-size: 16px;
  -moz-appearance: textfield;
  appearance: textfield;
}
/* − and + stand in for the browser's own arrows. */
.stepper input::-webkit-outer-spin-button,
.stepper input::-webkit-inner-spin-button { -webkit-appearance: none; margin: 0; }
/* A sentence beside a control, not a field title: none of the label caps. */
.addon-row label { color: var(--text); font-family: var(--font-ui); font-size: 14px; letter-spacing: normal; text-transform: none; }
.addon-row label strong { font-weight: 500; }
.addon-row label .muted { font-size: 12.5px; }

.addon-steps { list-style: none; margin: 0; padding: 16px 22px; display: flex; flex-direction: column; gap: 14px; border-top: 1px solid var(--border); }
.addon-steps li { display: flex; gap: 14px; }
.addon-steps .tip-n { flex: none; width: 26px; height: 26px; display: inline-flex; align-items: center; justify-content: center; border: 1px solid var(--border-strong); color: var(--muted); }
.addon-steps li > span:last-child { display: flex; flex-direction: column; gap: 2px; }
.addon-steps strong { font-weight: 500; }
.addon-steps .muted { font-size: 13px; }

/* The review, over the page. */
.addon-review { width: min(480px, calc(100vw - 32px)); }
/* It takes focus itself on opening (autofocus), so no link inside wears a
   focus ring before anyone has pressed a key; the dialog draws none. */
.addon-review:focus { outline: none; }
/* A state note at the head of a review: card updated, expired, missing. */
.addon-review .review-flag { margin: 0 24px 16px; font-size: 12.5px; }
.pay-details .addon-quote { margin-top: 16px; }

/* The plan-change review wraps everything in its form. */
.addon-review > form { padding: 0; }
.addon-review .review-head { display: flex; flex-direction: column; gap: 6px; padding: 22px 24px 16px; }
.addon-review .review-head h2 { margin: 0; font-size: 30px; line-height: 1; }
.addon-review .review-card { display: flex; flex-direction: column; gap: 10px; padding: 18px 24px 0; }
.addon-review .review-card p, .addon-review .review-note { margin: 0; font-size: 12.5px; line-height: 1.55; }
.addon-review .review-note { padding: 18px 24px 0; }
/* Cancelling: washed red, the way out set apart from the rest of the page. */
.bill-body.danger-zone { background: var(--rt-alert-bg); border-color: var(--rt-alert-line); }
.bill-body.danger-zone > .muted { flex: 1 1 320px; line-height: 1.6; }
.bill-body.danger-zone button.danger { flex: none; background: transparent; }

/* The card on file on Billing: chip, name, then the tag and button at the end. */
.bill-row.pay-method { justify-content: flex-start; align-items: center; gap: 14px; }
.pay-method .pay-text { flex: 1 1 auto; }
.pay-card { display: flex; align-items: center; gap: 14px; padding: 14px 16px; border: 1px solid var(--border-strong); background: var(--rt-hover); }
.addon-review .confirm-actions { margin: 0; padding: 20px 24px 22px; }

.addon-row {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: 12px 24px;
  padding: 16px 22px;
  border-top: 1px solid var(--border);
}
.addon-row label { display: flex; flex-direction: column; gap: 2px; }
.addon-row label strong { font-weight: 500; }
.addon-row label .muted { font-size: 12.5px; }
.addon-row input { width: 120px; text-align: right; font-family: var(--font-mono); }
.addon-foot {
  display: flex;
  flex-wrap: wrap;
  justify-content: flex-end;
  gap: 10px;
  padding: 16px 22px;
  border-top: 1px solid var(--border);
}
.addon-foot .button, .addon-foot button { min-height: var(--control-h); }

.addon-quote { margin: 0; }
.addon-quote > div {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  justify-content: space-between;
  gap: 8px 24px;
  padding: 14px 22px;
  border-top: 1px solid var(--border);
}
.addon-quote dt { color: var(--muted); font-family: var(--font-mono); font-size: 11px; letter-spacing: 0.12em; text-transform: uppercase; }
.addon-quote dd { margin: 0; display: inline-flex; align-items: baseline; gap: 8px; }
.addon-quote .total { background: var(--surface-2); }
.addon-quote .change { font-family: var(--font-mono); }
.addon-quote .total dd { font-family: var(--font-heading); font-size: 28px; font-weight: 700; line-height: 1; }
.addon-quote .total dd .muted { font-family: var(--font-ui); font-size: 12.5px; font-weight: 400; }
/* The amount on one line, what it covers under it. */
.addon-quote .total { flex-wrap: nowrap; align-items: flex-start; }
.addon-quote .total dt { flex: none; padding-top: 6px; }
.addon-quote .total dd { flex: 1 1 auto; min-width: 0; flex-direction: column; align-items: flex-end; gap: 4px; text-align: right; }
.addon-quote .total.credit dd { color: var(--ok-text); }

/* A cancelled subscription running out: on every page until it ends. */
.grace-banner {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 8px 12px;
  margin: 0 0 20px;
  padding: 10px 10px 10px 16px;
  border: 1px solid rgb(229 168 70 / 40%);
  background: var(--rt-warn-bg);
  font-size: 13px;
}
.grace-banner strong { font-weight: 500; }
.grace-banner > span:nth-child(2) { flex: 1 1 360px; }
.grace-dot { flex: none; width: 8px; height: 8px; background: var(--rt-warn); }
.grace-banner button { min-height: var(--control-h-sm); }

/* The setup fee: owed but not blocking, so a single quiet line. */
.fee-strip {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 8px 12px;
  margin: 0 0 20px;
  padding: 8px 8px 8px 14px;
  border: 1px solid var(--border);
  font-size: 12.5px;
}
.fee-strip strong { font-weight: 500; }
.fee-strip > span:nth-child(2) { flex: 1 1 320px; }
.fee-dot { flex: none; width: 6px; height: 6px; background: var(--rt-warn); }
.fee-strip button { min-height: var(--control-h-sm); }

/* ── Topping up ──────────────────────────────────────────────────────────── */

/* A row of one-press amounts, as a flex grid: each amount grows, so however
   many fit on a line, every line — the last one included — is filled edge to
   edge, where a grid would leave empty cells after an uneven count. The basis
   is what sets how many share a line: six across a wide card, three and three
   on a narrow one. */
.amounts {
  display: flex;
  flex-wrap: wrap;
  gap: 12px;
  margin: 20px 0 24px;
}
.amounts .amount { flex: 1 1 140px; min-width: 0; }
.amounts .amount { display: flex; flex-direction: column; gap: 8px; }
.amounts button { width: 100%; padding: 12px; font-size: 14px; }

/* What the amount above buys, in messages. Under the button rather than on it:
   the dollars are the choice being made, and the segments are the consequence
   — the same arrangement the plan cards use for their price and their
   allowance. */
.amount-segments {
  font-family: var(--font-mono);
  font-size: 11px;
  color: var(--muted);
  text-align: center;
}

/* Which card to charge. A fieldset rather than a styled div, so a screen
   reader reads the legend before either option and the pair is announced as
   one question. */
.card-choice { margin: 0 0 4px; }

.card-choice label {
  display: flex;
  align-items: flex-start;
  gap: 10px;
  margin: 0;
  padding: 8px 0;
  /* The label's own casing, not the uppercase every other label here takes:
     this one contains a card's name and a sentence, not a field title. */
  font-size: 13px;
  font-weight: 400;
  letter-spacing: normal;
  text-transform: none;
  color: var(--text);
  cursor: pointer;
}
.card-choice input[type="radio"] { margin: 3px 0 0; flex: none; }
.card-choice .choice { display: flex; flex-direction: column; gap: 2px; }
.card-choice .choice-title { display: flex; align-items: center; gap: 8px; }

/* The field for anything the row above does not offer. The margin its input
   carries by default would push the button out of line with it, so it is
   dropped here and the gap comes from the row instead. */
.custom-amount { border-top: 1px solid var(--border); padding-top: 20px; }
.custom-amount .row-actions { align-items: stretch; gap: 8px; }
.custom-amount input { flex: 1 1 200px; min-width: 0; margin-bottom: 0; }
.custom-amount button { flex: 1 0 auto; }
.custom-amount > p:last-child { margin-bottom: 0; }

/* ── Dialogs ─────────────────────────────────────────────────────────────── */

/* Native <dialog>, opened by the command attributes rather than by script: the
   CSP forbids inline script, and a confirmation that needs JavaScript to appear
   is one that silently stops confirming the day the script is blocked. */
.confirm {
  width: min(460px, calc(100vw - 32px));
  padding: 0;
  border: 1px solid var(--border);
  border-radius: var(--radius);
  background: var(--panel);
  color: var(--text);
  box-shadow: var(--shadow);
}
/* Dialogs fade and rise in, and fade back out, with their backdrop. Closing
   animates too: display and overlay are held back until the fade ends. */
.confirm, dialog.details {
  opacity: 0;
  transform: translateY(10px) scale(0.98);
  transition: opacity 0.18s ease, transform 0.18s ease,
    overlay 0.18s ease allow-discrete, display 0.18s ease allow-discrete;
}
.confirm[open], dialog.details[open] { opacity: 1; transform: none; }
@starting-style {
  .confirm[open], dialog.details[open] { opacity: 0; transform: translateY(10px) scale(0.98); }
}
.confirm::backdrop, .details::backdrop {
  background-color: transparent;
  transition: background-color 0.18s ease, overlay 0.18s ease allow-discrete, display 0.18s ease allow-discrete;
}
.confirm[open]::backdrop, .details[open]::backdrop { background-color: var(--rt-scrim); }
@starting-style {
  .confirm[open]::backdrop, .details[open]::backdrop { background-color: transparent; }
}
@media (prefers-reduced-motion: reduce) {
  .confirm, dialog.details, .confirm::backdrop, .details::backdrop { transition: none; }
}

/* A confirmation the server rendered arrives with open already on it, and a
   dialog opened that way is laid out in the flow of the page rather than over
   it — no backdrop, no centring. portal.js upgrades it to a real modal, but the
   markup has to read correctly before that runs and where it never does, so the
   modal's own geometry is written out here. A dialog showModal() opened matches
   these values already, so the rule costs the others nothing. */
.confirm[open] {
  position: fixed;
  inset: 0;
  margin: auto;
  z-index: 20;
  /* The rest of that geometry, which the browser gives a dialog showModal()
     opened and withholds from one merely carrying open. Without them a panel
     taller than the viewport — a phone held sideways, or one with the text
     scaled up — is clipped at both ends, and neither the heading nor the two
     buttons can be scrolled to, because a fixed box does not move with the
     page and an overflowing one gets no scrollbar of its own. */
  max-height: calc(100% - 32px);
  overflow: auto;
}

/* The way out of that same dialog is a link rather than a button, because it
   has to work with no script at all: it goes back to the page with nothing
   charged. Dressed as the quiet button it sits beside, so the two ways out of a
   confirmation do not look like two different kinds of thing. */
.confirm-actions a {
  display: inline-flex;
  align-items: center;
  min-height: var(--control-h);
  padding: 0 14px;
  border: 1px solid var(--border-strong);
  color: var(--text);
  font-weight: 500;
  font-size: 13px;
  text-decoration: none;
}
.confirm-actions a:hover { background: var(--rt-hover); }

.confirm form { padding: 24px; margin: 0; }
.confirm h2 { margin: 0 0 12px; }
.confirm p { margin: 0 0 12px; font-size: 12px; }

.confirm-actions {
  display: flex;
  gap: 10px;
  justify-content: flex-end;
  align-items: center;
  margin-top: 20px;
  flex-wrap: wrap;
}
.confirm-actions button { width: auto; }

/* Wider than a confirmation, and scrollable: it holds two JSON bodies and a
   table of retries, which no fixed height is going to fit. */
.details {
  width: min(760px, calc(100vw - 32px));
  max-height: calc(100vh - 64px);
  padding: 0;
  border: 1px solid var(--border);
  border-radius: var(--radius);
  background: var(--panel);
  color: var(--text);
  box-shadow: var(--shadow);
}

.details-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  padding: 14px 20px;
  border-bottom: 1px solid var(--border);
  /* Kept in view while the bodies below it scroll, so the Close button does not
     wander off the top of a long payload. */
  position: sticky;
  top: 0;
  background: var(--surface);
}
.details-head h2 { margin: 0; font-size: 18px; overflow-wrap: anywhere; }
.details-body { padding: 16px 20px 24px; overflow-y: auto; }
.details-body h3 { margin: 24px 0 8px; font-size: 12px; }

pre {
  margin: 0;
  padding: 14px 16px;
  background: var(--surface-2);
  border: 1px solid var(--border);
  font-family: var(--font-mono);
  font-size: 12px;
  line-height: 1.5;
  /* Long JSON scrolls sideways inside its own box rather than widening the
     dialog. */
  overflow-x: auto;
}

/* ── Page parts ──────────────────────────────────────────────────────────── */

/* A page's title row: the heading on the left, its one action on the right. */
.page-head {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  margin-bottom: 24px;
}
.page-head h1 { margin: 0; }

/* A link that acts as a button. The primary one is the brand red with the
   hard shadow, like button.primary; inverse is the ink slab. */
a.button.primary {
  gap: 8px;
  background: var(--primary);
  border-color: var(--primary);
  color: #fff;
  box-shadow: var(--press);
  transition: transform 0.12s, box-shadow 0.12s;
}
a.button.primary:hover { background: var(--primary); transform: translate(-2px, -2px); box-shadow: 6px 6px 0 rgb(240 74 46 / 35%); }
a.button.inverse { background: var(--text); border-color: var(--text); color: var(--bg); }
a.button.inverse:hover { background: var(--text); opacity: 0.9; }
a.button .ext { color: var(--faint); }

/* A state with its dot: green unless it says otherwise. */
.state { display: inline-flex; align-items: center; gap: 7px; font-weight: 500; }
.state::before {
  content: "";
  flex: none;
  width: 7px;
  height: 7px;
  border-radius: 50%;
  background: var(--rt-good);
}
.state.bad::before { background: var(--primary); }
.state.warn::before { background: var(--rt-warn); }

/* A panel: the card's fill and border, but its parts set their own padding,
   so rows inside it can run edge to edge with a rule between each. */
.panel {
  margin-bottom: 20px;
  background: var(--panel);
  border: 1px solid var(--border);
}
.panel.padded { display: flex; flex-direction: column; gap: 16px; padding: 20px 22px; }
.panel h2 { margin: 0; }
.panel-head { display: flex; flex-direction: column; gap: 12px; padding: 20px 22px 16px; }
.panel-head.row { flex-direction: row; align-items: center; justify-content: space-between; padding: 18px 22px; }
.panel-head p { margin: 0; line-height: 1.6; }
.panel-title { display: flex; align-items: center; justify-content: space-between; gap: 12px; flex-wrap: wrap; }
.panel-title strong { color: var(--text); font-weight: 500; }
.panel-body { display: flex; flex-direction: column; gap: 8px; padding: 0 22px 16px; }
.panel-foot { margin: 0; padding: 16px 22px; display: flex; flex-direction: column; gap: 10px; }
p.panel-foot { display: block; }
.pair-row { display: flex; justify-content: space-between; gap: 12px; padding: 13px 22px; border-top: 1px solid var(--border); }

.quiet-link { color: var(--muted); text-decoration: none; }
.quiet-link:hover { color: var(--text); }

/* A row that is a link: an icon or mark, two lines of text, a short answer at
   the end and a chevron. */
.row-link {
  display: flex;
  align-items: center;
  gap: 14px;
  padding: 13px 22px;
  border-top: 1px solid var(--border);
  color: var(--text);
  text-decoration: none;
}
.row-link:hover { background: var(--rt-hover); }
.row-text { display: flex; flex: 1; flex-direction: column; gap: 2px; min-width: 0; }
.row-title { font-size: 13.5px; font-weight: 500; }
.row-title.clip, .row-desc { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.row-title.clip { font-weight: 400; }
.row-desc { color: var(--muted); font-size: 12.5px; }
.row-desc code { font-size: 11.5px; }
.row-state { font-size: 12.5px; font-weight: 500; white-space: nowrap; color: var(--rt-good); }
.row-state.bad { color: var(--rt-accent-ink); }
.row-state.warn { color: var(--rt-warn); }
.row-state.todo, .row-state.unknown { color: var(--muted); }
.row-time { color: var(--muted); font-family: var(--font-mono); font-size: 12px; white-space: nowrap; }
.chev-right {
  flex: none;
  width: 7px;
  height: 7px;
  margin-right: 3px;
  border-top: 1.5px solid var(--faint);
  border-right: 1.5px solid var(--faint);
  transform: rotate(45deg);
}

/* A field that copies itself: the value in mono, and the word to press. */
.copy-field {
  justify-content: space-between;
  gap: 12px;
  width: 100%;
  min-height: var(--control-h);
  padding: 0 12px;
  background: var(--bg);
  border-color: var(--border);
  font-weight: 400;
}
.copy-field code { overflow: hidden; color: var(--text); text-overflow: ellipsis; white-space: nowrap; }
.copy-field .copy-label { flex: none; color: var(--muted); font-size: 11.5px; }

/* ── Overview ────────────────────────────────────────────────────────────── */

.account-head { display: flex; align-items: center; gap: 14px; }
.avatar.square {
  width: 46px;
  height: 46px;
  border-radius: 0;
  background: var(--primary);
  color: #fff;
  font-family: var(--font-heading);
  font-size: 21px;
  font-weight: 700;
  letter-spacing: 0.02em;
}
.account-meta { display: flex; flex-wrap: wrap; align-items: center; gap: 12px; margin: 5px 0 0; color: var(--muted); font-size: 13px; }
.account-meta .state { font-weight: 400; }
.account-meta .state::before { width: 6px; height: 6px; }

/* The first thing not in place, with the way to fix it. */
.next-step {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 16px;
  margin-bottom: 24px;
  padding: 16px 18px;
  background: var(--panel);
  border: 1px solid var(--border);
}
.next-step.alert { background: var(--rt-alert-bg); border-color: var(--rt-alert-line); }
.next-icon {
  position: relative;
  flex: none;
  width: 38px;
  height: 38px;
  border: 1px solid var(--border-strong);
}
.next-icon::after {
  content: "!";
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  color: var(--muted);
  font-weight: 600;
}
.next-step.alert .next-icon { border-color: var(--rt-alert-line); }
.next-step.alert .next-icon::after { color: var(--rt-accent-ink); }
.next-text { display: flex; flex: 1 1 340px; flex-direction: column; gap: 4px; min-width: 0; }
.next-label { color: var(--muted); font-family: var(--font-mono); font-size: 11px; letter-spacing: 0.14em; text-transform: uppercase; }
.next-step.alert .next-label { color: var(--rt-accent-ink); }
.next-text strong { font-size: 15px; font-weight: 500; }
.next-text > span:last-child { color: var(--muted); font-size: 13px; line-height: 1.55; }
/* 36px, as the design draws it: the card's own button, not a form control. */
.next-step .button { min-height: 36px; padding: 0 16px; }

/* Motion from the design: the card rises in, and a blocking one keeps a
   highlight sweeping across it, its icon ringing and its arrow nudging, so a
   stopped account reads as urgent at a glance. */
.next-step { position: relative; overflow: hidden; animation: rt-rise 0.5s cubic-bezier(0.2, 0.7, 0.2, 1) both; }
.next-step.alert::before {
  content: "";
  position: absolute;
  inset: 0 auto 0 0;
  width: 30%;
  pointer-events: none;
  background: linear-gradient(90deg, transparent, rgb(240 74 46 / 10%), transparent);
  animation: rt-sweep 4s ease-in-out 1s infinite;
  transform: translateX(-100%);
}
.next-step.alert .next-icon { animation: rt-ring 2s ease-out infinite; }
.next-step.alert .nudge { display: inline-block; animation: rt-nudge 2.4s ease-in-out infinite; }
@keyframes rt-rise { from { opacity: 0; transform: translateY(10px); } to { opacity: 1; transform: none; } }
@keyframes rt-sweep { 0% { transform: translateX(-100%); } 60%, 100% { transform: translateX(220%); } }
@keyframes rt-ring {
  0% { box-shadow: 0 0 0 0 rgb(240 74 46 / 45%); }
  70% { box-shadow: 0 0 0 12px rgb(240 74 46 / 0%); }
  100% { box-shadow: 0 0 0 0 rgb(240 74 46 / 0%); }
}
@keyframes rt-nudge { 0%, 80%, 100% { transform: translateX(0); } 88% { transform: translateX(4px); } }
@media (prefers-reduced-motion: reduce) {
  .next-step, .next-step.alert::before, .next-step.alert .next-icon, .next-step.alert .nudge { animation: none; }
}

.ov-grid { display: flex; flex-wrap: wrap; align-items: flex-start; gap: 20px; }
.ov-main { display: flex; flex: 2 1 560px; flex-direction: column; min-width: 0; }
.ov-side { display: flex; flex: 1 1 300px; flex-direction: column; min-width: 0; }

/* One bar per check, coloured by how it stands. */
.setup-bars { display: grid; grid-template-columns: repeat(4, 1fr); gap: 4px; }
.setup-bars span { height: 4px; background: var(--border); }
.setup-bars .ok { background: var(--rt-good); }
.setup-bars .bad { background: var(--primary); }
.setup-bars .warn { background: var(--rt-warn); }

.mark {
  display: flex;
  align-items: center;
  justify-content: center;
  flex: none;
  width: 22px;
  height: 22px;
  border-radius: 50%;
  border: 1.5px dashed var(--faint);
  color: #fff;
  font-size: 12px;
  font-weight: 700;
}
.mark.ok { background: var(--rt-good); border: 0; }
.mark.ok::after { content: "✓"; color: var(--bg); }
.mark.bad { background: var(--primary); border: 0; }
.mark.bad::after { content: "×"; }
.mark.warn { border: 1.5px solid var(--rt-warn); color: var(--rt-warn); }
.mark.warn::after { content: "!"; font-size: 11px; }

.snapshot {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 130px), 1fr));
  border-top: 1px solid var(--border);
}
.snapshot > div {
  display: flex;
  flex-direction: column;
  gap: 10px;
  padding: 16px 22px;
  border-bottom: 1px solid var(--border);
}
.snapshot > div + div { border-left: 1px solid var(--border); }
.snapshot .label { color: var(--muted); font-family: var(--font-mono); font-size: 11px; letter-spacing: 0.12em; text-transform: uppercase; }
.snapshot .big { font-family: var(--font-heading); font-size: 34px; font-weight: 600; line-height: 0.9; }
.snapshot .ok, .legend .ok::before, .dist .ok { color: var(--rt-good); fill: var(--rt-good); }
.snapshot .bad, .legend .bad::before, .dist .bad { color: var(--primary); fill: var(--primary); }
.snapshot .warn, .legend .warn::before, .dist .warn { color: var(--rt-warn); fill: var(--rt-warn); }
.dist { display: block; }
.legend { display: flex; flex-wrap: wrap; gap: 16px; color: var(--muted); font-size: 12.5px; }
.legend span { display: inline-flex; align-items: center; gap: 6px; }
.legend span::before { content: ""; width: 8px; height: 8px; background: currentcolor; }
.legend .ok::before { background: var(--rt-good); }
.legend .bad::before { background: var(--primary); }
.legend .warn::before { background: var(--rt-warn); }

.recent-row { padding-block: 12px; }
.recent-row .dot { flex: none; width: 8px; height: 8px; border-radius: 50%; background: var(--faint); }
.recent-row .dot.ok { background: var(--rt-good); }
.recent-row .dot.bad { background: var(--primary); }
.recent-row .dot.warn { background: var(--rt-warn); }

.side-head { display: flex; align-items: center; justify-content: space-between; gap: 10px; }
.side-head a { color: var(--rt-accent-ink); text-decoration: none; }
.side-label { display: flex; align-items: center; gap: 10px; color: var(--muted); font-family: var(--font-mono); font-size: 11px; letter-spacing: 0.12em; text-transform: uppercase; }
.side-icon {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 30px;
  height: 30px;
  background: var(--primary);
  color: #fff;
  font-family: var(--font-ui);
  font-size: 15px;
  font-weight: 600;
  letter-spacing: 0;
}
.side-icon.bolt::after { content: "ϟ"; }
.side-head .state { font-size: 12px; }
.side-figure { font-family: var(--font-heading); font-size: 52px; font-weight: 600; line-height: 0.9; }
.side-figure.low { color: var(--rt-accent-ink); }
/* The balance card once it has run out: framed in the accent, with the hard
   shadow the design gives the thing that needs doing. */
.panel.balance-out { border-color: var(--primary); box-shadow: 0 6px 0 var(--primary); margin-bottom: 26px; }
.ov-side .panel.padded:nth-child(2) .side-figure { font-size: 40px; }
.panel .row-actions { gap: 8px; }
.pairs { display: grid; grid-template-columns: 1fr auto; row-gap: 10px; margin: 0; }
.pairs dt { color: var(--muted); }
.pairs dd { margin: 0; text-align: right; }
.pairs code { color: var(--text); font-size: 12.5px; }
.period { display: flex; flex-direction: column; gap: 6px; }
.period svg { display: block; }
.period .track { fill: var(--border); }
.period .fill { fill: var(--text); }

/* ── Logs ────────────────────────────────────────────────────────────────── */

/* A page head whose subtitle sits under the title rather than beside an
   action. */
.page-head.stacked { flex-direction: column; align-items: flex-start; gap: 8px; }
.page-head.stacked .subtitle { margin: 0; }

/* Quick status filters: a square chip per status, a dot in its colour and the
   count beside the name. */
.chips { display: flex; flex-wrap: wrap; gap: 8px; margin: 0 0 16px; }
.chip {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  height: var(--control-h-sm);
  padding: 0 10px;
  border: 1px solid var(--border);
  color: var(--muted);
  font-size: 12.5px;
  text-decoration: none;
}
.chip::before { content: ""; width: 6px; height: 6px; border-radius: 50%; background: var(--faint); }
.chip.ok::before { background: var(--rt-good); }
.chip.warn::before { background: var(--rt-warn); }
.chip.bad::before { background: var(--primary); }
.chip:hover { color: var(--text); background: var(--rt-hover); }
.chip.active { color: var(--text); background: var(--rt-active); border-color: var(--border-strong); font-weight: 500; }
.chip .count { color: var(--faint); font-family: var(--font-mono); font-size: 11px; font-weight: 400; }

/* A log: the filters along the top of the panel, the table edge to edge, and
   the footnotes under it. */
.panel.log .filters { margin: 0; padding: 14px; border-bottom: 1px solid var(--border); }
.panel.log .filters input, .panel.log .filters select { padding-block: 0; }
.panel.log .table-wrap { margin: 0; }
.panel.log .filters > button, .panel.log .filters > .clear { flex: 0 0 auto; min-height: var(--control-h); }
.panel.log td.small { max-width: 11rem; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.panel.log table { border: 0; }
.panel.log th:first-child, .panel.log td:first-child { padding-left: 16px; }
.panel.log th:last-child, .panel.log td:last-child { padding-right: 16px; }
.panel.log > p { margin: 0; padding: 12px 16px; border-top: 1px solid var(--border); }
.panel.log > p.muted { color: var(--faint); font-size: 12px; }
.panel.log .pager { margin: 0; padding: 12px 16px; border-top: 1px solid var(--border); }

.log-foot { display: flex; flex-wrap: wrap; justify-content: space-between; gap: 8px 16px; color: var(--faint); font-size: 12px; }
.log-foot .hint[hidden] { display: none; }

tr[data-href] { cursor: pointer; }
tr[data-href]:focus-visible { outline: 2px solid var(--primary); outline-offset: -2px; }
td.route { white-space: nowrap; }
td.route code { color: var(--text); font-size: 12px; }
td.route .arrow { color: var(--faint); }
td.num code { color: var(--text); }
.panel.log .msg-body { max-width: 26rem; }
.panel.log .msg-body .clamp { -webkit-line-clamp: 1; line-clamp: 1; }

/* ── Billing ─────────────────────────────────────────────────────────────── */

.page-head .page-head { margin: 0; }

/* A settings section: what it is on the left, the thing itself on the right,
   ruled off from the next. Stacks once the two cannot sit side by side. */
.bill-section {
  display: flex;
  flex-wrap: wrap;
  gap: 20px 48px;
  padding: 32px 0;
  border-top: 1px solid var(--border);
}
.bill-intro { display: flex; flex: 0 1 260px; flex-direction: column; gap: 6px; }
.bill-intro h2 { margin: 0; }
.bill-intro p { margin: 0; color: var(--muted); line-height: 1.6; }
.bill-body { flex: 1 1 420px; min-width: 0; margin: 0; }

.bill-row {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  padding: 20px 24px;
}
.bill-body.bill-row { flex-direction: row; }
.bill-body.bill-row > .muted { flex: 1 1 280px; line-height: 1.6; }
.bill-figure { display: flex; flex-direction: column; align-items: flex-start; gap: 10px; }
.bill-name { display: flex; align-items: center; gap: 10px; }
.bill-name .side-figure { font-size: 36px; }
.bill-figure .side-figure { font-size: 48px; }
code.strong { color: var(--text); font-size: 13px; font-weight: 500; }

.bill-note { margin: 0; padding: 14px 24px; border-top: 1px solid var(--border); color: var(--muted); font-size: 12.5px; line-height: 1.6; }
.bill-body > .notice { margin: 0 24px 16px; }

.bill-period { display: flex; flex-direction: column; gap: 10px; padding: 18px 24px; border-top: 1px solid var(--border); }
.bill-period .pair { display: flex; flex-wrap: wrap; justify-content: space-between; gap: 12px; }
.bill-period svg { display: block; }
.bill-period .track { fill: var(--border); }
.bill-period .fill { fill: var(--text); }

.bill-cells { display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); border-top: 1px solid var(--border); }
.bill-cells > div { display: flex; flex-direction: column; gap: 6px; padding: 16px 24px; }
.bill-cells > div + div { border-left: 1px solid var(--border); }
.bill-cells .muted { font-size: 12.5px; }

.bill-body table { border: 0; }
.bill-body th:first-child, .bill-body td:first-child { padding-left: 20px; }
.bill-body th:last-child, .bill-body td:last-child { padding-right: 20px; }
a.button.small-link { min-height: var(--control-h-sm); padding: 0 10px; font-size: 12px; }

/* ── Keys and webhooks ───────────────────────────────────────────────────── */

.block { display: block; }
.state.small { font-size: 11.5px; font-weight: 400; color: var(--muted); }
.state.small::before { width: 5px; height: 5px; }
.row-title { display: block; }

/* A credential shown exactly once: loud on purpose, because the page that
   renders it is the only place it will ever exist. */
.secret-once {
  display: flex;
  flex-direction: column;
  gap: 14px;
  margin-bottom: 24px;
  padding: 18px 20px;
  background: var(--panel);
  border: 1px solid var(--primary);
  box-shadow: 0 6px 0 var(--primary);
}
.secret-head { display: flex; justify-content: space-between; gap: 12px; align-items: flex-start; }
.secret-head > div { display: flex; flex-direction: column; gap: 4px; }
.secret-head strong { display: flex; align-items: center; gap: 10px; font-size: 14px; font-weight: 600; }
.secret-head .muted { font-size: 12.5px; line-height: 1.55; }
.secret-row { display: flex; flex-wrap: wrap; gap: 8px; align-items: stretch; }
.secret-row code.secret { flex: 1 1 320px; margin: 0; padding: 10px 12px; background: var(--bg); border-color: var(--border-strong); }
button.inverse { background: var(--text); border-color: var(--text); color: var(--bg); }
button.inverse:hover { background: var(--text); opacity: 0.9; }

/* A key prefix that copies itself, in its table cell. */
.copy-inline {
  min-height: 0;
  padding: 0;
  gap: 8px;
  border: 0;
  background: none;
  font-weight: 400;
}
.copy-inline:hover { background: none; }
.copy-inline code { color: var(--text); font-size: 12.5px; }
.copy-inline:hover code { color: var(--rt-accent-ink); }
.copy-inline .copy-label { color: var(--faint); font-size: 11px; }
.key-prefix { color: var(--text); font-size: 12.5px; }
.key-id { display: block; margin-top: 2px; font-size: 11px; }

.mode { padding: 1px 7px; border: 1px solid var(--border-strong); color: var(--muted); font-size: 11px; }
.mode.live { border-color: var(--rt-good); color: var(--rt-good); }

.panel table { border: 0; }
.panel table th:first-child, .panel table td:first-child { padding-left: 20px; }
.panel table th:last-child, .panel table td:last-child { padding-right: 20px; }
.panel .panel-foot.small { font-size: 12px; color: var(--faint); border-top: 1px solid var(--border); }
td .row-actions { gap: 6px; }

.empty { display: flex; flex-direction: column; align-items: center; gap: 10px; padding: 56px 24px; text-align: center; }
.empty strong { font-size: 14px; font-weight: 500; }

/* Three numbered tips, sharing their borders. */
.tips {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
  gap: 1px;
  background: var(--border);
  border: 1px solid var(--border);
}
.tips > div { display: flex; flex-direction: column; gap: 6px; padding: 18px 20px; background: var(--surface); }
.tips strong { font-size: 13.5px; font-weight: 500; }
.tips .muted { font-size: 12.5px; line-height: 1.55; }
.tip-n { color: var(--rt-accent-ink); font-family: var(--font-mono); font-size: 11px; }

/* A choice between two modes, as two boxes side by side. */
fieldset.segmented { display: grid; grid-template-columns: 1fr 1fr; gap: 8px; padding: 0; border: 0; }
fieldset.segmented legend { padding: 0 0 8px; }
fieldset.segmented label, .choice {
  display: flex;
  align-items: flex-start;
  gap: 10px;
  margin: 0;
  padding: 12px;
  border: 1px solid var(--border);
  color: var(--text);
  font-family: var(--font-ui);
  font-size: 12.5px;
  letter-spacing: normal;
  text-transform: none;
  cursor: pointer;
}
fieldset.segmented label:has(:checked), .choice:has(:checked) { border-color: var(--primary); background: var(--rt-accent-bg); }
fieldset.segmented input, .choice input { margin: 2px 0 0; flex: none; }
fieldset.segmented span, .choice > span { display: flex; flex-direction: column; gap: 4px; color: var(--muted); }
fieldset.segmented strong, .choice strong { color: var(--text); font-size: 13.5px; font-weight: 500; }
.choice code { font-size: 11px; color: var(--faint); }
.confirm input[type="text"] { margin-bottom: 16px; }

.block-head { display: flex; align-items: center; justify-content: space-between; gap: 12px; margin-bottom: 8px; }
.block-head h2 { margin: 0; }
.block-note { margin: 0 0 14px; max-width: 80ch; line-height: 1.6; }

td.endpoint { display: flex; align-items: center; gap: 14px; min-width: 18rem; }
.ep-icon {
  display: flex;
  align-items: center;
  justify-content: center;
  flex: none;
  width: 34px;
  height: 34px;
  border: 1px solid var(--border-strong);
  color: var(--rt-accent-ink);
}
code.ep-url { color: var(--text); font-size: 13px; }
.tags { display: flex; flex-wrap: wrap; gap: 4px; }
.tag { padding: 2px 7px; border: 1px solid var(--border-strong); color: var(--muted); font-family: var(--font-mono); font-size: 11px; white-space: nowrap; }

.panel.empty-steps { flex-direction: row; flex-wrap: wrap; align-items: center; justify-content: space-between; }
.empty-steps > div { display: flex; flex: 1 1 220px; flex-direction: column; gap: 6px; }
.empty-steps strong { font-size: 14px; font-weight: 500; }
.empty-steps ol { display: flex; flex: 2 1 420px; flex-wrap: wrap; gap: 8px; margin: 0; padding: 0; list-style: none; }
.empty-steps li { display: flex; flex: 1 1 160px; flex-direction: column; gap: 6px; padding: 12px 14px; border: 1px solid var(--border); font-size: 12.5px; line-height: 1.5; }

.panel.register { margin: 0; flex: 3 1 520px; min-width: 0; }
.panel-form { display: flex; flex-direction: column; padding: 20px 22px 22px; border-top: 1px solid var(--border); }
.panel-form > button.primary { align-self: flex-start; margin-top: 4px; }
.optional { margin-left: 6px; color: var(--faint); letter-spacing: normal; text-transform: none; }

/* An input with a fixed word before it. */
.prefixed { display: flex; align-items: stretch; margin-bottom: 16px; border: 1px solid var(--border-strong); background: var(--input); }
.prefixed:focus-within { border-color: var(--primary); box-shadow: 0 0 0 1px var(--primary); }
.prefixed .prefix { display: flex; align-items: center; padding: 0 12px; border-right: 1px solid var(--border); color: var(--muted); font-family: var(--font-mono); font-size: 11.5px; }
.prefixed input { height: calc(var(--control-h) - 2px); margin: 0; border: 0; box-shadow: none; }
.prefixed input:focus-visible { box-shadow: none; }
fieldset.events { padding: 0; border: 0; }
fieldset.events legend { padding: 0 0 8px; }
.choice-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(100%, 220px), 1fr)); gap: 8px; margin-bottom: 18px; }

/* ── Back from Stripe checkout ───────────────────────────────────────────── */

.topup-return {
  max-width: 560px;
  margin: 20px auto 0;
  display: flex;
  flex-direction: column;
  gap: 24px;
}
.topup-return h1 { margin: -10px 0 0; font-size: 52px; line-height: 0.95; }
.topup-return .subtitle { margin: -12px 0 0; font-size: 15px; }

.tr-mark {
  display: inline-flex;
  align-items: center;
  gap: 10px;
  margin: 0;
  font-family: var(--font-label);
  font-size: 12px;
  font-weight: 600;
  letter-spacing: 0.08em;
  text-transform: uppercase;
}
.tr-mark.ok { color: var(--ok-text); }
.tr-mark.warn { color: var(--rt-warn); }
.tr-icon {
  width: 28px;
  height: 28px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  background: var(--rt-good-bg);
  border: 1px solid currentColor;
}
.tr-pulse { width: 10px; height: 10px; background: currentColor; animation: tr-pulse 1.4s ease-in-out infinite; }
@keyframes tr-pulse { 50% { opacity: 0.35; } }

.tr-figures.done { box-shadow: inset 0 3px 0 var(--ok-text); }
.tr-row {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 16px;
  padding: 22px 24px;
}
.tr-row + .tr-row { border-top: 1px solid var(--border); }
.tr-row .side-label { font-size: 10.5px; color: var(--faint); }
.tr-amount {
  font-family: var(--font-heading);
  font-size: 44px;
  font-weight: 700;
  line-height: 1;
  font-variant-numeric: tabular-nums;
}
.tr-amount.ok { color: var(--ok-text); }
.tr-amount.muted { color: var(--muted); }

/* Indeterminate: we do not know how long Stripe will take, only that it is
   usually seconds, so the bar sweeps rather than fills. */
.tr-sweep { height: 3px; background: var(--border); overflow: hidden; }
.tr-sweep span { display: block; width: 40%; height: 100%; background: var(--rt-warn); animation: tr-sweep 1.6s ease-in-out infinite; }
@keyframes tr-sweep { from { transform: translateX(-100%); } to { transform: translateX(250%); } }

.tr-status {
  margin: 0;
  padding: 12px 24px;
  border-top: 1px solid var(--border);
  color: var(--faint);
  font-family: var(--font-label);
  font-size: 11px;
  letter-spacing: 0.06em;
  text-transform: uppercase;
}

.tr-notes { margin: 0; padding: 0; list-style: none; display: flex; flex-direction: column; gap: 12px; color: var(--muted); font-size: 13px; }
.tr-notes li { display: flex; gap: 10px; }
.tr-notes li::before { content: "✓"; flex: none; color: var(--muted); }

.tr-actions { display: flex; flex-wrap: wrap; gap: 10px; }
.tr-actions .button { gap: 8px; }

.tr-foot { margin: 0; padding-top: 18px; border-top: 1px solid var(--border); color: var(--faint); font-size: 13px; }

@media (prefers-reduced-motion: reduce) {
  .tr-pulse, .tr-sweep span { animation: none; }
}

/* ── Topping up, redesigned ─────────────────────────────────────────────── */

.status-strip {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 8px 14px;
  padding: 8px 14px;
  border: 1px solid var(--border);
  font-size: 13px;
}
.status-strip.alert { background: var(--rt-alert-bg); border-color: var(--rt-alert-line); }
.status-strip > * + *::before { content: ""; display: inline-block; width: 1px; height: 14px; margin-right: 14px; vertical-align: middle; background: var(--border-strong); }
.status-strip .state::before { margin-right: 0; width: 6px; height: 6px; vertical-align: baseline; }
.status-strip .state { font-weight: 400; color: var(--muted); }
.status-strip strong { color: var(--text); font-weight: 600; }
.status-strip strong.low { color: var(--rt-accent-ink); }

.topup-grid { display: flex; flex-wrap: wrap; align-items: flex-start; gap: 24px; }
.panel.topup { flex: 1 1 520px; min-width: 0; margin: 0; }
.topup-part { display: flex; flex-direction: column; gap: 14px; margin: 0; padding: 24px 28px; border: 0; border-bottom: 1px solid var(--border); }
.topup-part legend, .part-label { float: left; width: 100%; margin-bottom: 14px; padding: 0; color: var(--muted); font-family: var(--font-label); font-size: 11px; letter-spacing: 0.12em; text-transform: uppercase; }
.part-label { float: none; margin: 0; }

.choice-list { display: flex; flex-direction: column; border: 1px solid var(--border); clear: both; }
.choice-list label { display: flex; align-items: flex-start; gap: 12px; margin: 0; padding: 14px 16px; color: var(--text); font-family: var(--font-ui); font-size: 13.5px; letter-spacing: normal; text-transform: none; cursor: pointer; }
.choice-list label + label { border-top: 1px solid var(--border); }
.choice-list label:has(:checked) { background: var(--rt-hover); box-shadow: inset 2px 0 0 var(--primary); }
.choice-list input { margin: 3px 0 0; flex: none; }

/* Pay with, as the design draws it: one list, a brand chip per card, a red
   ring for the chosen one and a lighter row behind it. */
.pay-list label { align-items: center; gap: 14px; padding: 16px 18px; }
.pay-list label:has(:checked) { box-shadow: none; }
.pay-list input[type="radio"] {
  appearance: none;
  flex: none;
  width: 18px;
  height: 18px;
  min-height: 0;
  margin: 0;
  padding: 0;
  border: 1.5px solid var(--border-strong);
  border-radius: 50%;
  background: transparent;
  cursor: pointer;
}
.pay-list input[type="radio"]:checked { border: 2px solid var(--primary); background: radial-gradient(var(--primary) 0 4px, transparent 4.5px); }
.pay-list input[type="radio"]:focus-visible { outline: 2px solid var(--primary); outline-offset: 2px; }
.pay-brand {
  flex: none;
  width: 40px;
  height: 26px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  background: var(--text);
  color: var(--bg);
  font-family: var(--font-ui);
  font-size: 10px;
  font-style: italic;
  font-weight: 800;
  letter-spacing: 0.02em;
  text-transform: uppercase;
}
.pay-brand.new { background: transparent; border: 1px dashed var(--border-strong); color: var(--muted); font-style: normal; font-weight: 400; font-size: 13px; }
.pay-text { display: flex; flex-direction: column; gap: 3px; min-width: 0; }
.pay-text .choice-title { display: flex; align-items: center; gap: 8px; font-size: 14px; font-weight: 500; }
.pay-note { margin: 0; }

.topup .amounts { display: grid; grid-template-columns: repeat(auto-fill, minmax(120px, 1fr)); gap: 8px; margin: 0; }
.topup .amounts .amount {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 4px;
  width: auto;
  min-height: 0;
  padding: 12px 14px;
  border-color: var(--border);
  font-weight: 400;
}
.topup .amounts .amount:hover { border-color: var(--primary); background: var(--rt-accent-bg); }
.amount-dollars { font-family: var(--font-heading); font-size: 26px; font-weight: 700; line-height: 1; }
.topup .amount-segments { text-align: left; color: var(--faint); }

.topup .custom-amount { padding-top: 24px; border-top: 0; }
.topup .custom-amount label { margin: 0; }
.big-amount { display: flex; align-items: center; gap: 10px; padding: 0 16px; border: 1px solid var(--border-strong); background: var(--bg); }
.big-amount:focus-within { border-color: var(--primary); box-shadow: 0 0 0 1px var(--primary); }
.big-amount .currency { color: var(--muted); font-family: var(--font-heading); font-size: 34px; font-weight: 600; }
.big-amount input { height: 64px; margin: 0; padding: 0; border: 0; background: none; font-family: var(--font-heading); font-size: 40px; font-weight: 600; }
.big-amount input:focus-visible { box-shadow: none; }
/* No spinner on the amount: it steps a dollar at a time, which nobody wants
   at $50, and it crowds the figure. */
.big-amount input { -moz-appearance: textfield; appearance: textfield; }
.big-amount input::-webkit-inner-spin-button,
.big-amount input::-webkit-outer-spin-button { -webkit-appearance: none; margin: 0; }
.topup .custom-amount p { margin: 0; }

.topup-foot { display: flex; flex-direction: column; gap: 12px; padding: 22px 28px; background: var(--rt-hover); }
.topup-foot button.primary { width: 100%; font-size: 14px; }
.topup-foot p { margin: 0; text-align: center; color: var(--faint); }

.facts-list { flex: 1 1 280px; margin: 0; }
.facts-list > div { display: flex; gap: 14px; padding: 18px 20px; }
.facts-list > div + div { border-top: 1px solid var(--border); }
.facts-list > div > span:last-child { display: flex; flex-direction: column; gap: 4px; min-width: 0; }
.facts-list strong { font-size: 13.5px; font-weight: 500; }
.facts-list .muted { font-size: 12.5px; line-height: 1.6; }
.fact-icon {
  display: flex;
  align-items: center;
  justify-content: center;
  flex: none;
  width: 30px;
  height: 30px;
  border: 1px solid var(--border-strong);
  color: var(--rt-accent-ink);
  font-weight: 600;
}

/* ── Analytics ───────────────────────────────────────────────────────────── */

.subtitle strong { color: var(--text); font-weight: 400; }
.head-controls { display: flex; flex-wrap: wrap; gap: 8px; }

/* Links that switch between a few values, drawn as one segmented control. */
.segment { display: flex; gap: 2px; padding: 3px; border: 1px solid var(--border); background: var(--bg); }
.segment a { display: flex; align-items: center; height: calc(var(--control-h-sm) - 0px); padding: 0 10px; color: var(--muted); font-size: 12.5px; font-weight: 500; text-decoration: none; }
.segment a:hover { color: var(--text); }
.segment a.active { color: var(--text); background: var(--rt-active); }

.filter-panel { margin-bottom: 20px; }
.filter-panel > summary {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  height: var(--control-h);
  padding: 0 12px;
  border: 1px solid var(--border-strong);
  color: var(--text);
  font-size: 13px;
  cursor: pointer;
  list-style: none;
}
.filter-panel > summary::-webkit-details-marker { display: none; }
.filter-panel > summary:hover { background: var(--rt-hover); }
.filter-panel[open] > summary { border-color: var(--faint); }
.filter-icon { width: 12px; height: 9px; border-top: 1.5px solid currentcolor; border-bottom: 1.5px solid currentcolor; position: relative; }
.filter-icon::after { content: ""; position: absolute; left: 3px; right: 3px; top: 2.5px; border-top: 1.5px solid currentcolor; }
.filter-panel .count { color: var(--rt-accent-ink); font-size: 11px; }
.filter-panel form { margin-top: 10px; padding: 16px; background: var(--panel); border: 1px solid var(--border); }
.filter-panel form p { flex-basis: 100%; margin: 0; }

.kpis { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(100%, 220px), 1fr)); gap: 12px; margin-bottom: 20px; }
.kpi {
  display: flex;
  flex-direction: column;
  gap: 12px;
  padding: 18px 20px;
  background: var(--panel);
  border: 1px solid var(--border);
  color: var(--text);
  text-decoration: none;
}
.kpi:hover { border-color: var(--border-strong); }
.kpi-label { display: flex; justify-content: space-between; color: var(--muted); font-family: var(--font-mono); font-size: 11px; letter-spacing: 0.12em; text-transform: uppercase; }
.kpi-label span { color: var(--faint); letter-spacing: 0; }
.kpi-value { font-family: var(--font-heading); font-size: 40px; font-weight: 600; line-height: 0.9; }
.kpi-value.ok { color: var(--rt-good); }
.kpi-value.bad { color: var(--rt-accent-ink); }
.kpi .muted { font-size: 12.5px; }
.kpi svg, .an-bar svg, .an-money svg { display: block; }
.track { fill: var(--border); }
svg .ink { fill: var(--text); }
svg .ok { fill: var(--rt-good); }
svg .bad { fill: var(--primary); }
svg .warn { fill: var(--rt-warn); }
svg rect[class=""] { fill: var(--faint); }

.an-grid { display: flex; flex-wrap: wrap; align-items: stretch; gap: 20px; }
.an-status { flex: 3 1 480px; min-width: 0; margin: 0; }
.an-money { flex: 2 1 320px; min-width: 0; margin: 0; }
.an-bar { padding: 4px 22px 16px; }
.swatch { flex: none; width: 10px; height: 10px; background: var(--faint); }
.swatch.ok { background: var(--rt-good); }
.swatch.bad { background: var(--primary); }
.swatch.warn { background: var(--rt-warn); }
.an-count { display: flex; flex-direction: column; align-items: flex-end; gap: 3px; }
.an-count .big { font-family: var(--font-heading); font-size: 26px; font-weight: 600; line-height: 0.9; }
.an-count code { font-size: 11.5px; }
.an-money .panel-body { padding-top: 4px; }
.an-money .pair { display: flex; justify-content: space-between; }
.money-row { display: flex; align-items: center; gap: 14px; padding: 14px 22px; border-top: 1px solid var(--border); }
.money-row.total { border-top-color: var(--border-strong); }
.money-row .op { flex: none; width: 14px; color: var(--faint); font-family: var(--font-mono); }
.money-row .row-desc { white-space: normal; }
code.money { color: var(--text); font-family: var(--font-heading); font-size: 26px; font-weight: 600; line-height: 0.9; }
code.money.ok { color: var(--rt-good); }
code.money.hot { color: var(--rt-accent-ink); }
.an-foot { margin-top: 16px; }

/* ── Numbers ─────────────────────────────────────────────────────────────── */

.num-grid { display: flex; flex-wrap: wrap; align-items: stretch; gap: 20px; margin-bottom: 20px; }
.num-grid .figures { flex: 2 1 420px; margin: 0; }
.figure .value.warn { color: var(--rt-warn); }
.picker { flex: 2 1 380px; margin: 0; }
.picker strong { font-size: 13.5px; font-weight: 500; }
.picker p { margin: 0; font-size: 12.5px; line-height: 1.6; }
.flow { display: flex; flex-wrap: wrap; gap: 8px; margin: 0; padding: 0; list-style: none; counter-reset: none; }
.flow li { display: flex; flex: 1 1 140px; flex-direction: column; gap: 4px; padding: 10px 12px; border: 1px solid var(--border); font-size: 12.5px; }
.flow li span { color: var(--faint); font-size: 11.5px; }
.flow li span.hot { color: var(--rt-accent-ink); }
.num-line { display: flex; align-items: center; gap: 8px; }

/* ── Balance history ─────────────────────────────────────────────────────── */

.ledger-day { display: flex; justify-content: space-between; gap: 12px; padding: 14px 24px; background: var(--surface-2); }
.ledger-day strong { font-weight: 500; }
.ledger-row { display: flex; align-items: center; gap: 14px; padding: 12px 24px; border-top: 1px solid var(--border); }
.ledger-row:hover { background: var(--rt-hover); }
.ledger-icon {
  display: flex;
  align-items: center;
  justify-content: center;
  flex: none;
  width: 30px;
  height: 30px;
  border: 1px solid var(--border-strong);
  color: var(--muted);
  font-weight: 600;
}
.ledger-icon.in { border-color: var(--rt-good); color: var(--rt-good); background: var(--rt-good-bg); }
.ledger-row .row-desc a { color: var(--muted); }
.ledger-row .row-desc a:hover code { color: var(--text); }
.ledger-amount { display: flex; flex: none; flex-direction: column; align-items: flex-end; gap: 3px; }
.ledger-amount code { color: var(--text); font-size: 13px; font-weight: 500; }
.ledger-amount code.ok { color: var(--rt-good); }
.ledger-amount code.after { color: var(--faint); font-size: 11.5px; font-weight: 400; }

/* ── Profile ─────────────────────────────────────────────────────────────── */

.who-head { display: flex; flex-wrap: wrap; align-items: center; gap: 16px 20px; padding: 22px 24px; }
.who-head .row-text { flex: 1 1 240px; gap: 6px; }
.who-name { font-size: 34px; }
.who-head .tag { display: inline-flex; gap: 8px; padding: 6px 10px; font-family: var(--font-ui); font-size: 12.5px; color: var(--text); }
.bill-cells .copy-inline code { font-size: 13px; }
.block { margin-bottom: 28px; }
.block-head.stacked { flex-direction: column; align-items: flex-start; gap: 6px; }

.session-row { display: flex; flex-wrap: wrap; align-items: center; gap: 12px 18px; padding: 16px 20px; }
.session-row + .session-row { border-top: 1px solid var(--border); }
.session-row .row-text { flex: 1 1 200px; gap: 5px; }
.session-row .row-title { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; }
.session-times { flex: 0 0 340px; display: grid; grid-template-columns: 1fr 1fr; column-gap: 24px; row-gap: 4px; margin: 0; font-size: 13px; }
.session-times dt { color: var(--faint); font-family: var(--font-mono); font-size: 10.5px; letter-spacing: 0.12em; text-transform: uppercase; }
.session-times dd { margin: 0; }
.session-row form { margin: 0; }
.session-action { display: flex; flex: 0 0 84px; justify-content: flex-end; }
.why { margin-top: 10px; color: var(--muted); font-size: 12.5px; }
.why summary { color: var(--faint); cursor: pointer; }
.why p { max-width: 90ch; margin: 8px 0 0; line-height: 1.6; }

.pw-form { max-width: 520px; border-top: 1px solid var(--border); }
.pw-form .field { display: flex; flex-direction: column; margin-bottom: 4px; }
.pw-form .pw-field, .pw-form input[type="password"] { margin-bottom: 8px; }
.strength { display: flex; align-items: center; gap: 10px; margin-bottom: 12px; font-size: 12px; color: var(--muted); }
.strength[hidden], .match-note[hidden] { display: none; }
.strength .bars { display: grid; flex: 1; grid-template-columns: repeat(4, 1fr); gap: 4px; }
.strength .bars span { height: 4px; background: var(--border); }
.strength[data-score="1"] .bars span:nth-child(-n+1),
.strength[data-score="2"] .bars span:nth-child(-n+2) { background: var(--rt-warn); }
.strength[data-score="3"] .bars span:nth-child(-n+3),
.strength[data-score="4"] .bars span { background: var(--rt-good); }
.strength[data-score="0"] { color: var(--rt-accent-ink); }
.match-note { display: block; margin-bottom: 12px; }
.match-note.ok { color: var(--rt-good); }
.match-note.bad { color: var(--rt-accent-ink); }

/* ── Logs ────────────────────────────────────────────────────────────────── */

/* A choice of a few ranges, as radios drawn as one segmented control. The
   filter button applies it, like every other field in the row. */
.filters > fieldset.seg-radio, fieldset.seg-radio { display: flex; flex: 0 0 auto; margin: 0; padding: 0; border: 1px solid var(--border-strong); }
.seg-radio label { display: flex; margin: 0; cursor: pointer; font-family: var(--font-ui); font-size: 12.5px; letter-spacing: normal; text-transform: none; }
.seg-radio label + label { border-left: 1px solid var(--border-strong); }
.seg-radio input { position: absolute; opacity: 0; pointer-events: none; }
.seg-radio span { display: flex; align-items: center; padding: 0 12px; color: var(--muted); white-space: nowrap; }
.seg-radio label:hover span { color: var(--text); background: var(--rt-hover); }
.seg-radio input:checked + span { color: var(--bg); background: var(--text); font-weight: 500; }
.seg-radio input:focus-visible + span { outline: 2px solid var(--primary); outline-offset: -2px; }

/* A checkbox drawn as a switch. Still a checkbox to the form and to a screen
   reader, which role="switch" names correctly. */
.filters > label.check.switch { flex: 0 0 auto; gap: 10px; color: var(--text); font-size: 13px; }
label.switch input,
.panel.log .filters label.switch input {
  position: relative;
  flex: none;
  width: 28px;
  height: 16px;
  min-height: 0;
  padding: 0;
  margin: 0;
  appearance: none;
  background: var(--border-strong);
  border: 0;
  cursor: pointer;
  transition: background 0.15s;
}
label.switch input::after {
  content: "";
  position: absolute;
  top: 2px;
  left: 2px;
  width: 12px;
  height: 12px;
  border-radius: 50%;
  background: #fff;
  transition: left 0.15s;
}
label.switch input:checked,
.panel.log .filters label.switch input:checked { background: var(--primary); }
label.switch input:checked::after { left: 14px; }
@media (prefers-reduced-motion: reduce) { label.switch input, label.switch input::after { transition: none; } }

@media (max-width: 600px) {
  .seg-radio label { flex: 1 1 0; min-width: 0; }
  .seg-radio span { justify-content: center; width: 100%; padding: 8px 4px; font-size: 12px; white-space: normal; text-align: center; }
}

.panel.log .pager { justify-content: flex-end; color: var(--faint); font-size: 12px; }
.panel.log .empty a { color: var(--rt-accent-ink); font-size: 12.5px; font-weight: 500; text-decoration: none; }

/* ── One message ─────────────────────────────────────────────────────────── */

.back-link { display: inline-block; margin-bottom: 10px; color: var(--muted); font-size: 12.5px; text-decoration: none; }
.back-link:hover { color: var(--text); }
.page-head .copy-inline code { color: var(--muted); }

.next-step.failure { align-items: flex-start; }
.next-step.failure h2.next-label { margin: 0; font-family: var(--font-mono); font-size: 11px; font-weight: 400; letter-spacing: 0.14em; }
.next-step.failure strong { font-size: 14px; }
.next-step.failure .pairs { max-width: 520px; margin-top: 8px; font-size: 13px; }
.next-step.failure .pairs dd { text-align: left; }
.next-step.failure .pairs { grid-template-columns: 140px 1fr; }

/* The text as a bubble, the way the recipient's phone draws it. */
.bubble-wrap { display: flex; flex-direction: column; align-items: flex-start; gap: 12px; padding: 4px 22px 22px; }
pre.message-text.bubble {
  max-width: min(100%, 520px);
  margin: 0;
  padding: 12px 16px;
  background: var(--text);
  color: var(--bg);
  border: 0;
  border-radius: 14px 14px 14px 0;
  font-family: var(--font-ui);
  font-size: 14px;
  line-height: 1.5;
}
.bubble-wrap .part-label { margin: 6px 0 0; font-family: var(--font-label); font-size: 11px; font-weight: 400; letter-spacing: 0.12em; color: var(--muted); }

.ov-main .timeline { margin: 0; padding: 0 22px 8px; }
.timeline .tl { padding-left: 24px; }
.timeline .tl::before { top: 15px; width: 10px; height: 10px; border-radius: 50%; }
.timeline .tl-head .tl-label { font-weight: 500; }
.timeline .tl-pending .tl-label { font-weight: 400; }
.tl-at { color: var(--faint); font-size: 11.5px; }

.md-stats { display: grid; grid-template-columns: 1fr; }
.md-stats > * { display: flex; flex-direction: column; gap: 8px; padding: 16px 22px; color: var(--text); text-decoration: none; }
.md-stats > * + * { border-top: 1px solid var(--border); }
.md-stats a:hover { background: var(--rt-hover); }
.md-stats .big { font-family: var(--font-heading); font-size: 34px; font-weight: 600; line-height: 0.9; }

.detail-list { margin: 0; }
.detail-list > div { display: flex; justify-content: space-between; gap: 16px; padding: 11px 22px; border-top: 1px solid var(--border); font-size: 13px; }
.detail-list dt { color: var(--muted); }
.detail-list dd { margin: 0; text-align: right; overflow-wrap: anywhere; }
.detail-list code { color: var(--text); font-size: 12.5px; }


/* ── Message drawer ────────────────────────────────────────────────────── */

/* The deliveries come twice in the markup: the table on the page, the compact
   list in the drawer. */
.delivery-list { display: none; margin: 0; padding: 0; list-style: none; }
.delivery-list li { display: flex; align-items: center; justify-content: space-between; gap: 12px; padding: 12px 22px; border-top: 1px solid var(--border); }
.delivery-list code { color: var(--text); font-size: 12.5px; }

tr.selected td { background: var(--rt-active); }
tr.selected td:first-child { box-shadow: inset 2px 0 0 var(--primary); }

/* A native modal dialog, pinned to the right edge and the full height. */
dialog.drawer {
  position: fixed;
  inset: 0 0 0 auto;
  width: min(520px, 100vw);
  max-width: 100vw;
  height: 100dvh;
  max-height: 100dvh;
  margin: 0;
  padding: 0;
  border: 0;
  border-left: 1px solid var(--border-strong);
  background: var(--bg);
  color: var(--text);
  box-shadow: var(--rt-pop-shadow);
  overflow: hidden;
}
/* Slides in from the right and back out, the backdrop fading with it. */
dialog.drawer {
  transform: translateX(100%);
  transition: transform 0.2s ease, overlay 0.2s ease allow-discrete, display 0.2s ease allow-discrete;
}
dialog.drawer[open] { display: flex; flex-direction: column; transform: none; }
@starting-style { dialog.drawer[open] { transform: translateX(100%); } }
dialog.drawer::backdrop {
  background-color: transparent;
  transition: background-color 0.2s ease, overlay 0.2s ease allow-discrete, display 0.2s ease allow-discrete;
}
dialog.drawer[open]::backdrop { background-color: var(--rt-scrim); }
@starting-style { dialog.drawer[open]::backdrop { background-color: transparent; } }

.drawer-head {
  display: flex;
  align-items: center;
  gap: 8px;
  flex: none;
  height: var(--topbar-h);
  padding: 0 16px;
  border-bottom: 1px solid var(--border);
}
.drawer-nav { display: flex; gap: 4px; margin-right: auto; }
.drawer-nav button { min-width: var(--control-h-sm); padding: 0; }
.drawer-x { width: var(--control-h-sm); min-height: var(--control-h-sm); padding: 0; border-color: var(--border); color: var(--muted); }
.drawer-body { flex: 1; overflow-y: auto; padding: 20px; }
.drawer-body[aria-busy="true"] { opacity: 0.5; transition: opacity 0.15s 0.15s; }

/* The message page, restacked for a column a third of the width. */
.drawer .page-head { margin-bottom: 16px; }
.drawer h1 { font-size: 28px; }
.drawer .ov-grid { flex-direction: column; flex-wrap: nowrap; align-items: stretch; gap: 0; }
/* Both columns' panels in one list, in reading order: the figures, what was
   sent, what happened to it, the particulars, then the webhooks. */
.drawer .ov-main, .drawer .ov-side { display: contents; }
.drawer .md-stats { order: 1; }
.drawer .ov-main > section:nth-child(1) { order: 2; }
.drawer .ov-main > section:nth-child(2) { order: 3; }
.drawer .ov-side > section:nth-child(2) { order: 4; }
.drawer .ov-main > section:nth-child(3) { order: 5; }
.drawer .md-stats { grid-template-columns: repeat(3, 1fr); }
.drawer .md-stats > * + * { border-top: 0; border-left: 1px solid var(--border); }
.drawer .md-stats > * { padding: 14px 16px; }
.drawer .md-stats .big { font-size: 24px; }
.drawer .panel-head.row, .drawer .panel-head { padding: 14px 16px; }
.drawer .bubble-wrap { padding: 0 16px 16px; }
.drawer .detail-list > div, .drawer .delivery-list li, .drawer .panel-foot { padding-left: 16px; padding-right: 16px; }
.drawer .ov-main .timeline { padding: 0 16px 4px; }
.drawer .next-step.failure .pairs { grid-template-columns: 110px 1fr; }
.drawer .deliveries-table { display: none; }
.drawer .delivery-list { display: block; }

@media (prefers-reduced-motion: reduce) {
  dialog.drawer, dialog.drawer::backdrop { transition: none; }
}

/* ── Message log, as designed ────────────────────────────────────────────── */

.view-tabs .count {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 20px;
  height: 18px;
  margin-left: 8px;
  padding: 0 6px;
  background: var(--rt-active);
  color: var(--muted);
  font-size: 11px;
  font-weight: 500;
}
.view-tabs a { font-size: 13.5px; font-weight: 500; }

.chip-row { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; margin-bottom: 16px; }
.chip-row .chips { margin: 0; }
.view-switch { display: flex; gap: 2px; margin-left: auto; padding: 3px; border: 1px solid var(--border); background: var(--bg); }
.view-switch[hidden] { display: none; }
.view-switch button { min-height: var(--control-h-sm); padding: 0 9px; gap: 6px; border: 0; color: var(--muted); font-size: 12.5px; }
.view-switch button[aria-pressed="true"] { background: var(--rt-active); color: var(--text); }

/* The search field: an icon and a borderless input inside a box drawn like
   an input. */
.filters > .search { display: flex; flex: 2 1 260px; align-items: center; gap: 8px; padding: 0 11px; border: 1px solid var(--border-strong); background: var(--input); color: var(--faint); }
.filters > .search:focus-within { border-color: var(--primary); box-shadow: 0 0 0 1px var(--primary); }
.filters > .search input { flex: 1; min-width: 0; margin: 0; padding: 0; border: 0; background: none; box-shadow: none; }
.filters > .search input { height: calc(var(--control-h) - 2px); }
.panel.log .filters input[name="from"], .panel.log .filters input[name="to"] { font-family: var(--font-mono); font-size: 12.5px; }

/* One line per message: fixed columns, the text cut with an ellipsis. */
table.fixed { table-layout: fixed; }
table.fixed th.c-status { width: 132px; }
table.fixed th.c-route { width: 236px; }
table.fixed th.c-key { width: 170px; }
table.fixed th.c-seg { width: 56px; }
table.fixed th.c-cost { width: 86px; }
table.fixed th.c-when { width: 120px; }
table.fixed td { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
table.fixed td.msg-body { max-width: none; min-width: 0; }
table.fixed td.msg-body .clamp { display: inline; white-space: nowrap; }
.js-rows .msg-id, .js-rows .err-line, .js-rows .attach { display: none; }
.err-line { display: block; }
.panel.log td.small { max-width: none; }

/* The list view: one card per message. */
.msg-list { display: none; margin: 0; padding: 0; list-style: none; }
.panel.log.view-list .table-wrap { display: none; }
.panel.log.view-list .msg-list { display: block; }
.msg-list li {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-start;
  gap: 10px 18px;
  padding: 14px 18px;
  border-top: 1px solid var(--border);
  cursor: pointer;
}
.msg-list li:first-child { border-top: 0; }
.msg-list li:hover { background: var(--rt-hover); }
.msg-list li.selected { background: var(--rt-active); box-shadow: inset 2px 0 0 var(--primary); }
.msg-list li:focus-visible { outline: 2px solid var(--primary); outline-offset: -2px; }
.ml-status { flex: 0 0 96px; padding-top: 1px; }
.ml-main { display: flex; flex: 1 1 300px; flex-direction: column; gap: 6px; min-width: 0; }
.ml-body { overflow: hidden; font-size: 13.5px; line-height: 1.45; text-overflow: ellipsis; white-space: nowrap; }
.ml-meta { display: flex; flex-wrap: wrap; align-items: center; gap: 4px 10px; min-width: 0; color: var(--muted); font-size: 12px; }
.ml-meta code { color: var(--text); font-size: 12px; }
.ml-meta .arrow, .ml-meta .sep { color: var(--faint); }
.ml-meta .tag { font-family: var(--font-ui); }
.ml-id { text-decoration: none; }
.ml-id code { color: var(--faint); font-size: 11.5px; }
.ml-id:hover code { color: var(--text); }
.ml-side { display: flex; flex: 0 0 auto; flex-direction: column; align-items: flex-end; gap: 6px; margin-left: auto; font-size: 13px; text-align: right; white-space: nowrap; }
.ml-side code { color: var(--muted); font-size: 12px; }
.ml-chips { display: flex; gap: 6px; }
.ml-chips code { padding: 1px 7px; background: var(--rt-active); }
.ml-chips code.ink { color: var(--text); }

/* The footer: the count on the left, the hint and the arrows on the right. */
.panel.log .log-foot { align-items: center; margin: 0; padding: 10px 18px; border-top: 1px solid var(--border); color: var(--muted); font-size: 12.5px; }
.foot-right { display: flex; align-items: center; gap: 14px; }
.log-foot .hint { color: var(--faint); }
.pager-arrows { display: flex; gap: 4px; }
.pager-arrows > * {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 28px;
  height: 28px;
  border: 1px solid var(--border);
  color: var(--text);
  font-size: 15px;
  text-decoration: none;
}
.pager-arrows a:hover { background: var(--rt-hover); border-color: var(--border-strong); }
.pager-arrows span { color: var(--faint); opacity: 0.5; }

.empty-icon {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 40px;
  height: 40px;
  background: var(--surface-2);
  border: 1px solid var(--border);
  color: var(--muted);
}
.panel.log .empty a.button { font-size: 12.5px; }

.log-note { display: flex; gap: 10px; margin: 0; padding: 14px 18px; border-top: 1px solid var(--border); color: var(--muted); font-size: 12.5px; line-height: 1.6; }
.log-note svg { flex: none; margin-top: 3px; color: var(--faint); }
.panel.log > p.log-note { padding: 14px 18px; }

/* ── Profile, as designed ────────────────────────────────────────────────── */

/* A page whose design column is narrower than the console's. */
.narrow-page { margin: 0; display: flex; flex-direction: column; gap: 28px; }
.narrow-page > * { margin: 0; }

.who-card .who-head { padding: 24px; gap: 18px 24px; border-bottom: 1px solid var(--border); }
.who-avatar {
  display: flex;
  align-items: center;
  justify-content: center;
  flex: none;
  width: 64px;
  height: 64px;
  border-radius: 50%;
  background: var(--rt-active);
  border: 1px solid var(--border-strong);
  font-family: var(--font-heading);
  font-size: 28px;
  font-weight: 700;
}
.who-card .side-label { font-size: 10.5px; letter-spacing: 0.14em; color: var(--faint); }
.who-name { font-family: var(--font-heading); font-size: 30px; font-weight: 700; line-height: 1; letter-spacing: 0.01em; }
.who-account { display: inline-flex; align-items: center; gap: 8px; height: 30px; padding: 0 12px; border: 1px solid var(--border-strong); font-family: var(--font-label); font-size: 12px; }
.who-account .muted { color: var(--faint); }
.who-cells { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(100%, 260px), 1fr)); }
.who-cells > div { display: flex; flex-direction: column; gap: 8px; padding: 16px 24px; font-size: 13.5px; }
.who-cells > div + div { box-shadow: inset 1px 0 0 var(--border); }
.who-cells .side-label { font-size: 10.5px; color: var(--faint); }

/* An ID that copies itself, its button a small bordered tag. */
.copy-id { justify-content: flex-start; gap: 10px; min-height: 0; padding: 0; border: 0; background: none; font-weight: 400; }
.copy-id:hover { background: none; color: var(--rt-accent-ink); }
.copy-id code { overflow: hidden; color: inherit; font-family: var(--font-label); font-size: 12.5px; text-overflow: ellipsis; white-space: nowrap; }
.copy-id .copy-label { display: flex; align-items: center; flex: none; height: 20px; padding: 0 6px; border: 1px solid var(--border-strong); color: var(--muted); font-family: var(--font-label); font-size: 10.5px; }

.block-head { display: flex; flex-wrap: wrap; align-items: flex-end; justify-content: space-between; gap: 12px; margin-bottom: 12px; }
.block-title { display: flex; flex-direction: column; gap: 6px; }
.block-title h2 { margin: 0; font-size: 22px; }
.block-title p, .block-title > .muted { margin: 0; max-width: 80ch; font-size: 13px; line-height: 1.6; }
.block-head form { margin: 0; }
.block-head button { min-height: var(--control-h); font-size: 12.5px; }
button:disabled { opacity: 0.45; cursor: not-allowed; }
button:disabled:hover { background: transparent; border-color: var(--border-strong); }

.session-row { gap: 12px 24px; padding: 16px 20px; }
.session-icon { display: flex; align-items: center; justify-content: center; flex: none; width: 40px; height: 40px; background: var(--primary); color: #fff; }
.session-row .row-title { font-size: 14px; }
.session-row .row-desc code { color: var(--muted); font-family: var(--font-label); font-size: 12px; }
.tag-good { display: inline-flex; align-items: center; gap: 6px; height: 20px; padding: 0 8px; background: var(--rt-good-bg); color: var(--rt-good); font-family: var(--font-label); font-size: 10.5px; font-weight: 400; letter-spacing: 0.06em; text-transform: uppercase; }
.tag-good::before { content: ""; width: 5px; height: 5px; border-radius: 50%; background: currentcolor; }
.session-times { flex: 0 0 auto; grid-template-columns: 170px 110px; column-gap: 28px; row-gap: 3px; font-size: 12.5px; }
.session-times dt { font-family: var(--font-label); font-size: 10.5px; letter-spacing: 0.1em; }
.session-times .state.small { color: var(--text); font-size: 12.5px; }
.session-times .state.small::before { width: 6px; height: 6px; }
button.signout-one { min-height: var(--control-h-sm); padding: 0 12px; font-size: 12.5px; }
button.signout-one:hover { border-color: var(--rt-alert-line); background: var(--rt-alert-bg); color: var(--rt-accent-ink); }

/* The password, behind its button. */
.pw-block > summary { list-style: none; cursor: pointer; margin-bottom: 0; }
.pw-block > summary::-webkit-details-marker { display: none; }
.pw-block .pw-open { display: inline-flex; align-items: center; min-height: var(--control-h); padding: 0 14px; background: var(--text); color: var(--bg); font-size: 12.5px; font-weight: 500; }
.pw-block > summary:hover .pw-open { opacity: 0.9; }
.pw-block > summary:focus-visible { outline: none; }
.pw-block > summary:focus-visible .pw-open { outline: 2px solid var(--primary); outline-offset: 2px; }
.pw-block[open] .pw-open { display: none; }
.pw-block[open] > summary { margin-bottom: 12px; }
.pw-form.panel { display: flex; flex-direction: column; max-width: none; padding: 0; border-top: 1px solid var(--border); }
.pw-fields { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(100%, 240px), 1fr)); gap: 16px; padding: 20px; }
.pw-fields .field { margin: 0; }
.pw-fields input[type="password"] { margin-bottom: 8px; }
.pw-fields .pw-field { margin-bottom: 8px; }
.pw-fields .pw-field > input { margin-bottom: 0; }
.pw-fields label { font-size: 11px; }
.pw-foot { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: 12px 20px; padding: 14px 20px; border-top: 1px solid var(--border); }
.pw-foot p { flex: 1 1 360px; margin: 0; line-height: 1.6; }

/* Analytics: the filters button sits beside the range switch, and its panel
   opens under the heading. The button is lifted into the heading's row. */
.an-top { position: relative; }
.an-top > .page-head { align-items: flex-end; margin-bottom: 20px; }
.an-top .head-controls { margin-right: 98px; }
.an-top .filter-panel > summary { position: absolute; top: calc(-20px - var(--control-h)); right: 0; height: var(--control-h); }
.an-top .filter-panel { position: relative; }
.an-top .filter-panel:not([open]) { margin: 0; }
.an-top .filter-panel[open] { margin-bottom: 20px; }
.an-top .filter-panel form { margin-top: 0; }
.an-table { margin-top: 20px; }
.an-table table { border: 0; }
.an-table th, .an-table td { padding-left: 22px; padding-right: 22px; }
.an-table code { color: var(--faint); font-size: 12.5px; }
.an-table code.ink { color: var(--text); }
.an-table tfoot th { border-top: 1px solid var(--border-strong); border-bottom: 0; color: var(--muted); }

/* Numbers. */
.next-step.warn { background: var(--rt-warn-bg); border-color: rgb(229 168 70 / 30%); }
.next-step.warn .next-icon { border-color: rgb(229 168 70 / 40%); }
.next-step.warn .next-icon::after { content: "⚠"; color: var(--rt-warn); font-weight: 400; }
.flow { flex-wrap: nowrap; align-items: stretch; }
.flow li { flex: 1 1 0; color: var(--text); }
.flow li.flow-arrow { flex: none; align-self: center; padding: 0; border: 0; color: var(--faint); }
.flow li.hot { border-color: var(--primary); }
.flow li.hot span { color: var(--rt-accent-ink); }
@media (max-width: 700px) { .flow { flex-wrap: wrap; } .flow li.flow-arrow { display: none; } }

/* A table headed in small capitals, as the keys table is in the design. */
thead.caps-head th { background: none; font-size: 11.5px; font-weight: 400; letter-spacing: 0.04em; text-transform: uppercase; color: var(--faint); }

/* Webhooks: the form, and how deliveries work beside it. */
.wh-grid { display: flex; flex-wrap: wrap; align-items: flex-start; gap: 16px; margin-top: 24px; }
.wh-aside { flex: 2 1 280px; margin: 0; }
.wh-aside strong { font-size: 13.5px; font-weight: 500; }
.wh-aside ul { display: flex; flex-direction: column; gap: 14px; margin: 0; padding: 0; list-style: none; }
.wh-aside li { display: flex; gap: 12px; color: var(--muted); font-size: 12.5px; line-height: 1.6; }
.wh-aside .fact-icon { width: 26px; height: 26px; color: var(--muted); font-size: 12px; }
.wh-aside a { color: var(--rt-accent-ink); font-size: 12.5px; font-weight: 500; text-decoration: none; }

/* Field names in sentence case, as the design's forms label them. */
.plain-labels label:not(.choice) { font-family: var(--font-ui); font-size: 12.5px; font-weight: 500; letter-spacing: normal; text-transform: none; color: var(--text); }
.plain-labels legend { font-family: var(--font-ui); font-size: 12.5px; font-weight: 500; letter-spacing: normal; text-transform: none; color: var(--text); }
.plain-labels .optional { color: var(--faint); font-weight: 400; }
.plain-labels .prefixed { margin-bottom: 6px; }
.field-hint { margin-bottom: 18px; color: var(--faint); font-size: 12px; }
.choice .tags { gap: 4px; }
.choice .tag { padding: 1px 6px; font-size: 11px; }
.form-foot { display: flex; justify-content: flex-end; margin: 0 -22px -22px; padding: 14px 22px; border-top: 1px solid var(--border); }

/* Top up, as designed: the amount large, the presets as chips under it. */
.amount-part { display: flex; flex-direction: column; gap: 18px; padding: 28px 28px 22px; }
.amount-part .big-amount-wrap { order: 1; display: flex; flex-direction: column; gap: 10px; }
.amount-part .amounts { order: 2; display: flex; flex-wrap: wrap; gap: 8px; }
.amount-part > p { order: 3; margin: 0; }
.amount-part .part-label { margin: 0; }
.amount-part .big-amount { align-items: baseline; gap: 6px; padding: 0 0 14px; border: 0; border-bottom: 2px solid var(--border-strong); background: none; box-shadow: none; }
.amount-part .big-amount:focus-within { border-color: var(--primary); box-shadow: none; }
.amount-part .big-amount .currency { font-family: var(--font-heading); font-size: clamp(44px, 12vw, 64px); font-weight: 700; line-height: 1; color: var(--faint); }
.amount-part .big-amount input { flex: 1 1 120px; min-width: 0; height: auto; font-size: clamp(44px, 12vw, 64px); font-weight: 700; line-height: 1; }
.amount-part .estimate { flex: none; color: var(--muted); font-family: var(--font-label); font-size: 12.5px; white-space: nowrap; }
.topup .amount-part .amounts .amount {
  flex: none;
  width: auto;
  flex-direction: row;
  align-items: center;
  justify-content: center;
  /* The design draws these presets a size up from the other small controls. */
  min-height: 36px;
  padding: 0 14px;
  border-color: var(--border);
  background: var(--bg);
  color: var(--text);
  font-family: var(--font-label);
  font-size: 13px;
  font-weight: 500;
  line-height: 1;
  transition: border-color 0.12s, background-color 0.12s;
}
.topup .amount-part .amounts .amount:hover { border-color: var(--border-strong); background: var(--bg); }
.topup .amount-part .amounts .amount.active { background: var(--primary); border-color: var(--primary); color: #fff; }
.topup-foot .after { display: flex; justify-content: space-between; align-items: baseline; font-size: 13px; }
.topup-foot .after[hidden] { display: none; }
.topup-foot .after code { color: var(--text); font-family: var(--font-label); font-size: 15px; font-weight: 600; }
.topup .card-choice { border-top: 1px solid var(--border); }
.facts-list .fact-icon { border: 0; background: var(--primary); color: #fff; }

/* Billing's plan card, as designed. */
.bill-body > .bill-row, .plan-change > summary.bill-row { align-items: flex-start; padding: 22px 24px; }
.bill-name .side-figure { font-size: 36px; font-weight: 600; line-height: 0.9; }
.bill-name .pill { height: 22px; padding: 0 9px; font-size: 12px; line-height: 22px; }
.bill-name .pill::before { width: 6px; height: 6px; }
.bill-price { font-size: 13.5px; }
.bill-price code.strong { font-size: 13.5px; }
.bill-includes { display: flex; flex-wrap: wrap; gap: 6px; }
.bill-includes > span { display: inline-flex; align-items: baseline; gap: 6px; padding: 4px 10px; border: 1px solid var(--border); color: var(--muted); font-size: 12.5px; }
.bill-includes code.strong { font-size: 12.5px; color: var(--text); }
.bill-figure { display: flex; flex-direction: column; gap: 10px; }

/* Change plan: the whole head is the disclosure's summary, but only its
   button answers the pointer, so the plan's name stays plain text. */
.plan-change > summary { list-style: none; cursor: default; pointer-events: none; }
.plan-change > summary::-webkit-details-marker { display: none; }
.plan-change > summary .plan-toggle {
  display: inline-flex;
  align-items: center;
  flex: none;
  min-height: var(--control-h);
  padding: 0 14px;
  border: 1px solid var(--border-strong);
  color: var(--text);
  font-size: 13px;
  font-weight: 500;
  pointer-events: auto;
  cursor: pointer;
}
.plan-change > summary .plan-toggle:hover { background: var(--rt-hover); border-color: var(--faint); }
.plan-change > summary:focus-visible { outline: none; }
.plan-change > summary:focus-visible .plan-toggle { outline: 2px solid var(--primary); outline-offset: 2px; }
.plan-change[open] .plan-toggle { background: var(--rt-hover); border-color: var(--faint); }
.plan-info {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 12px;
  margin: 0 24px 18px;
  padding: 12px 14px;
  background: var(--surface-2);
  border: 1px solid var(--border);
}
.plan-info > span { flex: 1 1 300px; color: var(--muted); font-size: 12.5px; line-height: 1.6; }
.plan-info .button { min-height: var(--control-h-sm); font-size: 12.5px; }
.bill-period { padding: 18px 24px; }
.bill-period .pair { font-size: 13px; }
.bill-period .pair.muted.small { font-size: 12.5px; }

/* ── Collapsed sidebar ───────────────────────────────────────────────────── */

/* The fold button, half over the sidebar's right edge. The sidebar lets it
   out; its nav scrolls on its own. */
.sidebar { overflow: visible; }
.nav-collapse {
  position: absolute;
  top: 19px;
  right: -11px;
  z-index: 2;
  width: 22px;
  min-height: 0;
  height: 22px;
  padding: 0;
  border: 1px solid var(--border-strong);
  background: var(--bg);
  color: var(--faint);
}
.nav-collapse[hidden] { display: none; }
.nav-collapse:hover { color: var(--text); border-color: var(--primary); background: var(--bg); }
.nav-collapsed .nav-collapse svg { transform: rotate(180deg); }

@media (min-width: 961px) {
  .nav-collapsed { --sidebar-w: 60px; }
  .nav-collapsed .sidebar-head { justify-content: center; padding: 0; }
  .nav-collapsed .brand { width: 27px; overflow: hidden; }
  .nav-collapsed .logo { max-width: none; }
  .nav-collapsed .sidebar nav { padding: 14px 10px; }
  .nav-collapsed .sidebar .nav-label { height: 1px; margin: 12px 8px; overflow: hidden; background: var(--border); color: transparent; }
  .nav-collapsed .sidebar .nav-label:first-child { display: none; }
  .nav-collapsed .sidebar nav a,
  .nav-collapsed .sidebar .nav-group > summary,
  .nav-collapsed .sidebar-foot a { justify-content: center; padding: 0; font-size: 0; }
  .nav-collapsed .nav-group > a,
  .nav-collapsed .nav-group .chev,
  .nav-collapsed .nav-group .nav-text,
  .nav-collapsed .nav-badge,
  .nav-collapsed .sidebar-foot .nav-text,
  .nav-collapsed .sidebar-foot .ext { display: none; }
}
@media (max-width: 960px) {
  .nav-collapse { display: none; }
}

/* ── Narrow ──────────────────────────────────────────────────────────────── */

@media (max-width: 700px) {
  main { padding: 28px 16px 64px; }
  h1 { font-size: 30px; }
  .card { padding: 18px 16px; }
}

@media (max-width: 600px) {
  /* The filter row stacks rather than squeezing three controls onto a phone,
     every control the full width of the column. flex: none because in a
     column the row's basis would be read as a height. */
  .filters { flex-direction: column; }
  .filters > :not(.visually-hidden),
  .filters > button,
  .filters > .clear { flex: none; width: 100%; }

  /* Let the row run to both screen edges. A card cut off by the viewport rather
     than by a margin is the clearest possible signal that it scrolls — and the
     padding keeps the first card aligned with the text above it. */
  .plans {
    margin-inline: -16px;
    padding-inline: 16px;
    scroll-padding-inline-start: 16px;
  }
}

/* Snapping fights anyone who scrolls in steps rather than flicks, and a reduced
   -motion preference is the best signal we get for that. The buttons' press is
   decoration too. */
@media (prefers-reduced-motion: reduce) {
  .plans { scroll-snap-type: none; }
  button, button:hover, button:active { transition: none; transform: none; }
}

/* A hover hint, drawn from data-tip: the theme switch says what it is set to
   and what a click does. Shown on keyboard focus too, below its button and
   aligned to its right edge so it never runs off the screen. */
[data-tip] { position: relative; }
[data-tip]:hover::after, [data-tip]:focus-visible::after {
  content: attr(data-tip);
  position: absolute;
  top: calc(100% + 8px);
  right: 0;
  z-index: 40;
  padding: 6px 9px;
  white-space: nowrap;
  background: var(--surface);
  border: 1px solid var(--border-strong);
  color: var(--text);
  font-family: var(--font-ui);
  font-size: 12px;
  font-weight: 400;
  letter-spacing: 0;
  line-height: 1.4;
  text-transform: none;
  box-shadow: var(--shadow);
  pointer-events: none;
}
