/* Vasl — the site, wearing the application's face.
 *
 * The same palette, the same two easing curves, the same durations, the same
 * fonts. Somebody who has used the program should recognise this page as the
 * same thing before they read a word of it, and somebody who arrives here first
 * should not be surprised by what they install.
 *
 * The fonts are served from here rather than from Google. This audience is in
 * Iran: a stylesheet that blocks on fonts.googleapis.com is a page that renders
 * in a fallback face or does not render at all, and the files are 40KB.
 */

/* ═══ faces ════════════════════════════════════════════════════════════ */

@font-face {
  font-family: "Vazirmatn"; font-style: normal; font-weight: 400;
  font-display: swap; src: url("/static/fonts/Vazirmatn-Regular.woff2") format("woff2");
}
@font-face {
  font-family: "Vazirmatn"; font-style: normal; font-weight: 500;
  font-display: swap; src: url("/static/fonts/Vazirmatn-Medium.woff2") format("woff2");
}
@font-face {
  font-family: "Vazirmatn"; font-style: normal; font-weight: 600;
  font-display: swap; src: url("/static/fonts/Vazirmatn-SemiBold.woff2") format("woff2");
}
@font-face {
  font-family: "IBM Plex Sans"; font-style: normal; font-weight: 400;
  font-display: swap; src: url("/static/fonts/IBMPlexSans-Regular.woff2") format("woff2");
}
@font-face {
  font-family: "IBM Plex Sans"; font-style: normal; font-weight: 600;
  font-display: swap; src: url("/static/fonts/IBMPlexSans-SemiBold.woff2") format("woff2");
}
@font-face {
  font-family: "IBM Plex Mono"; font-style: normal; font-weight: 400;
  font-display: swap; src: url("/static/fonts/IBMPlexMono-Regular.woff2") format("woff2");
}
@font-face {
  font-family: "IBM Plex Mono"; font-style: normal; font-weight: 500;
  font-display: swap; src: url("/static/fonts/IBMPlexMono-Medium.woff2") format("woff2");
}

/* ═══ the application's tokens ═════════════════════════════════════════ */

:root {
  color-scheme: dark;

  --bg: #0a0a0a;      --sunken: #080808;  --surface: #131313;
  --raised: #1a1a1a;  --hover: #202020;
  --fg: #f5f5f5;      --muted: #a8a8a8;   --faint: #858585;
  --border-soft: #1e1e20; --border: #232326; --border-lit: #5a5a5e;

  /* Gold, and only gold. In the application the metal is silver at rest and
     gold when the tunnel is up; a page has no tunnel, so it wears the state
     the product is sold for. */
  --metal: #ffd700;
  --metal-deep: #e0b800;
  --metal-line: rgba(255, 215, 0, 0.46);
  --metal-soft: rgba(255, 215, 0, 0.14);
  --metal-ink: #111111;
  --metal-wash: #1b1a12;

  --q-good: #6ee7a8;  --q-fair: #ffcc66;  --q-poor: #ff6b6b;

  --font-fa: "Vazirmatn", "Noto Sans Arabic", system-ui, sans-serif;
  --font-la: "IBM Plex Sans", system-ui, sans-serif;
  --font-mono: "IBM Plex Mono", ui-monospace, "Courier New", monospace;

  --u: 4px;
  --max: 1180px;
  --radius: 16px;
  --radius-sm: 10px;
  --radius-xs: 6px;

  /* The two curves and three durations the application uses, unchanged. */
  --ease: cubic-bezier(0.16, 1, 0.3, 1);
  --ease-soft: cubic-bezier(0.32, 0.72, 0, 1);
  --t-fast: 140ms;
  --t-base: 260ms;
  --t-slow: 420ms;

  --lift: 0 18px 44px rgba(0, 0, 0, 0.55);
  --glow-sm: 0 0 22px rgba(255, 215, 0, 0.16);
}

* { box-sizing: border-box; }
html, body { margin: 0; background: var(--bg); }

body {
  color: var(--fg);
  font-family: var(--font-fa);
  font-size: 15px;
  line-height: 1.8;
  -webkit-font-smoothing: antialiased;
  /* The same low glow behind the top of the page that the link panel carries
     in the application. */
  background:
    radial-gradient(90% 60% at 50% -10%, rgba(255, 215, 0, 0.05), transparent 60%),
    var(--bg);
  background-attachment: fixed;
}

.num, code, pre, .mono {
  font-family: var(--font-mono);
  font-variant-numeric: tabular-nums;
  font-feature-settings: "tnum" 1;
}
.latin { font-family: var(--font-la); }

/* Arabic script joins; tracking pulls the joins apart. Latin runs keep theirs. */
h1, h2, h3, p, ul, ol { margin: 0; }
:lang(fa) h1, :lang(fa) h2, :lang(fa) h3 { letter-spacing: normal; }

a { color: var(--metal); text-decoration: none; transition: color var(--t-fast) var(--ease-soft); }
a:hover { color: var(--metal-deep); }

:focus-visible { outline: 2px solid var(--metal); outline-offset: 3px; border-radius: var(--radius-xs); }
::selection { background: var(--metal-soft); }

.wrap { max-width: var(--max); margin: 0 auto; padding: 0 calc(var(--u) * 7); }

/* ═══ the bar at the top ═══════════════════════════════════════════════ */

.top {
  position: sticky; top: 0; z-index: 30;
  border-bottom: 1px solid var(--border-soft);
  background: color-mix(in oklch, var(--bg) 82%, transparent);
  backdrop-filter: blur(14px);
}
.top .wrap { display: flex; align-items: center; gap: calc(var(--u) * 6); min-height: 62px; }
.brand {
  display: inline-flex; align-items: center; gap: calc(var(--u) * 2);
  color: var(--fg); font-size: 17px; font-weight: 600; letter-spacing: 0.04em;
}
.brand svg { width: 17px; height: 17px; color: var(--metal); }
nav.main { display: flex; gap: calc(var(--u) * 5); margin-inline-end: auto; }
nav.main a { color: var(--muted); font-size: 14px; }
nav.main a:hover, nav.main a.current { color: var(--fg); }
nav.main a.current { text-shadow: 0 0 18px var(--metal-soft); }
.top-actions { display: flex; align-items: center; gap: calc(var(--u) * 2.5); }

/* ═══ buttons ══════════════════════════════════════════════════════════ */

.button {
  display: inline-flex; align-items: center; justify-content: center;
  gap: calc(var(--u) * 2);
  padding: calc(var(--u) * 2.75) calc(var(--u) * 5);
  min-height: 40px;
  border: 1px solid var(--border-lit); border-radius: 999px;
  background: transparent; color: var(--fg);
  font: inherit; font-size: 14px; font-weight: 500; cursor: pointer;
  transition: background var(--t-base) var(--ease-soft),
              border-color var(--t-base) var(--ease-soft),
              color var(--t-base) var(--ease-soft),
              scale 120ms var(--ease-soft);
}
.button:hover { background: var(--raised); border-color: var(--fg); }
/* Two hundredths under the press, exactly as the application does it. */
.button:active { scale: 0.978; }
.button.solid {
  background: var(--metal); border-color: var(--metal); color: var(--metal-ink);
  font-weight: 600; box-shadow: var(--glow-sm);
}
.button.solid:hover { background: var(--metal-deep); border-color: var(--metal-deep); color: var(--metal-ink); }
.button.quiet { border-color: var(--border); color: var(--muted); }
.button.quiet:hover { color: var(--fg); border-color: var(--border-lit); background: transparent; }
.button.big { padding: calc(var(--u) * 3.5) calc(var(--u) * 7); min-height: 50px; font-size: 15px; }

/* ═══ sections ═════════════════════════════════════════════════════════ */

section { padding: calc(var(--u) * 22) 0; }
section + section { border-top: 1px solid var(--border-soft); }

.eyebrow {
  font-family: var(--font-mono); font-size: 11px;
  letter-spacing: 0.18em; text-transform: uppercase; color: var(--muted);
}
.metal-rule {
  width: 32px; height: 2px; border-radius: 2px;
  background: var(--metal); box-shadow: var(--glow-sm);
  margin-bottom: calc(var(--u) * 4);
}

h1 { font-size: clamp(30px, 4.4vw, 50px); font-weight: 600; line-height: 1.22; letter-spacing: -0.02em; }
h2 { font-size: clamp(22px, 2.6vw, 30px); font-weight: 600; line-height: 1.32; letter-spacing: -0.01em; }
h3 { font-size: 17px; font-weight: 600; }
.lead { color: var(--muted); font-size: 17px; max-width: 62ch; margin-top: calc(var(--u) * 4); }
p + p { margin-top: calc(var(--u) * 3); }

.hero { padding: calc(var(--u) * 28) 0 calc(var(--u) * 20); }
.cta { display: flex; flex-wrap: wrap; gap: calc(var(--u) * 3); margin-top: calc(var(--u) * 8); }

/* ═══ cards, figures, lists ════════════════════════════════════════════ */

.grid {
  display: grid; gap: calc(var(--u) * 4);
  grid-template-columns: repeat(auto-fit, minmax(258px, 1fr));
  margin-top: calc(var(--u) * 9);
}
.item {
  padding: calc(var(--u) * 6);
  border: 1px solid var(--border); border-radius: var(--radius);
  background: linear-gradient(180deg, var(--surface), var(--sunken));
  transition: border-color var(--t-base) var(--ease-soft),
              transform var(--t-slow) var(--ease),
              box-shadow var(--t-slow) var(--ease);
}
.item:hover { border-color: var(--metal-line); transform: translateY(-3px); box-shadow: var(--glow-sm); }
.item h3 { margin-bottom: calc(var(--u) * 2); }
.item p { color: var(--muted); font-size: 14px; }

.figures {
  display: grid; gap: calc(var(--u) * 4);
  grid-template-columns: repeat(auto-fit, minmax(190px, 1fr));
  margin-top: calc(var(--u) * 9);
}
.figure {
  padding: calc(var(--u) * 6);
  border: 1px solid var(--border-soft); border-radius: var(--radius);
  background: var(--sunken);
}
.figure .value { font-size: 30px; font-weight: 500; color: var(--metal); line-height: 1.2; }
.figure .value.plain { color: var(--fg); }
.figure .label { color: var(--muted); font-size: 13px; margin-top: calc(var(--u) * 1.5); }
.unmeasured { color: var(--faint); }

.steps { list-style: none; counter-reset: step; padding: 0; margin-top: calc(var(--u) * 6); }
.steps li {
  counter-increment: step;
  position: relative; padding-inline-start: calc(var(--u) * 12);
  padding-block: calc(var(--u) * 4);
  border-top: 1px solid var(--border-soft);
}
.steps li::before {
  content: counter(step);
  position: absolute; inset-inline-start: 0; top: calc(var(--u) * 4.5);
  width: 28px; height: 28px; border-radius: 50%;
  display: grid; place-items: center;
  border: 1px solid var(--metal-line); color: var(--metal);
  font-family: var(--font-mono); font-size: 13px;
}
.steps li p { color: var(--muted); font-size: 14px; margin-top: calc(var(--u) * 1.5); }

.callout {
  margin-top: calc(var(--u) * 6);
  padding: calc(var(--u) * 4) calc(var(--u) * 5);
  border: 1px solid var(--metal-line); border-radius: var(--radius-sm);
  background: var(--metal-wash); font-size: 14px;
}
.callout.bad { border-color: color-mix(in oklch, var(--q-poor) 50%, transparent); background: color-mix(in oklch, var(--q-poor) 13%, var(--surface)); }

.accent { color: var(--metal); }
.key { color: var(--muted); }

table { width: 100%; border-collapse: collapse; margin-top: calc(var(--u) * 6); font-size: 14px; }
th, td { text-align: start; padding: calc(var(--u) * 3) calc(var(--u) * 2); border-bottom: 1px solid var(--border-soft); }
th { color: var(--muted); font-weight: 500; font-size: 13px; }

/* ═══ forms ════════════════════════════════════════════════════════════ */

.card {
  width: 100%; max-width: 400px; margin: calc(var(--u) * 20) auto;
  padding: calc(var(--u) * 9);
  border: 1px solid var(--border); border-radius: var(--radius);
  background: linear-gradient(180deg, var(--surface), var(--sunken));
  box-shadow: var(--lift);
}
.card h1 { font-size: 26px; }
.field { display: flex; flex-direction: column; gap: calc(var(--u) * 1.5); margin-top: calc(var(--u) * 5); }
.field label { color: var(--muted); font-size: 13px; }
.input {
  width: 100%; font: inherit; font-size: 15px; font-family: var(--font-la);
  color: var(--fg); background: var(--sunken);
  border: 1px solid var(--border-lit); border-radius: var(--radius-sm);
  padding: calc(var(--u) * 3) calc(var(--u) * 3.5);
  transition: border-color var(--t-base) var(--ease-soft),
              background var(--t-base) var(--ease-soft),
              box-shadow var(--t-base) var(--ease-soft);
}
.input:hover { border-color: var(--fg); }
.input:focus { outline: none; border-color: var(--metal); background: var(--surface); box-shadow: 0 0 0 3px var(--metal-soft); }
.field .note { color: var(--faint); font-size: 12px; }
.tabs {
  display: grid; grid-template-columns: 1fr 1fr;
  border: 1px solid var(--border); border-radius: var(--radius-sm);
  overflow: hidden; margin-top: calc(var(--u) * 6);
}
.tabs a {
  padding-block: calc(var(--u) * 2.75); text-align: center;
  color: var(--muted); font-size: 13px;
  transition: background var(--t-base) var(--ease-soft), color var(--t-base) var(--ease-soft);
}
.tabs a + a { border-inline-start: 1px solid var(--border); }
.tabs a:hover { background: var(--raised); color: var(--fg); }
.tabs a.current { background: var(--hover); color: var(--fg); font-weight: 600; }
.card .button { width: 100%; margin-top: calc(var(--u) * 7); }
.card-foot { margin-top: calc(var(--u) * 5); text-align: center; color: var(--faint); font-size: 13px; }

/* ═══ the foot ═════════════════════════════════════════════════════════ */

.bottom { border-top: 1px solid var(--border-soft); padding: calc(var(--u) * 10) 0; color: var(--faint); font-size: 13px; }
.bottom .wrap { display: flex; flex-wrap: wrap; gap: calc(var(--u) * 4); justify-content: space-between; }

/* ═══ arriving ═════════════════════════════════════════════════════════
   Sections rise into place as they are reached. The same curve and the same
   distance the application's sheets and toasts use — a page that moves the way
   the program moves. `no-js` is the default so nothing depends on the script:
   without it every section is simply already in place. */

.reveal { opacity: 0; translate: 0 18px; }
.js .reveal { transition: opacity var(--t-slow) var(--ease-soft), translate var(--t-slow) var(--ease); }
.js .reveal.seen, html:not(.js) .reveal { opacity: 1; translate: 0 0; }

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: 0.01ms !important; animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
  }
  .reveal { opacity: 1; translate: 0 0; }
}

@media (max-width: 720px) {
  .top .wrap { gap: calc(var(--u) * 4); }
  nav.main { gap: calc(var(--u) * 3.5); font-size: 13px; }
  section { padding: calc(var(--u) * 14) 0; }
  .hero { padding: calc(var(--u) * 16) 0 calc(var(--u) * 12); }
}

/* ═══ a phone ══════════════════════════════════════════════════════════
   Measured at 390x844, which is what an iPhone reports, and the width this
   audience mostly arrives on. Three things were wrong there and each of them
   pushed the page sideways:

     · the bar's three groups — mark, navigation, buttons — were one row that
       could not fit, and in a right-to-left document the overflow goes off the
       *left* edge, so the one button that matters, «ساخت حساب», was the part
       that got cut;
     · tables are as wide as their columns, and the status table's verdict
       column was the one off screen — the column somebody opens that page for;
     · and a page that scrolls sideways at all reads as broken, whatever is on
       it.

   So the bar wraps onto two rows, the navigation scrolls on its own line
   rather than pushing the page, and every table scrolls inside its own box. */
@media (max-width: 620px) {
  .wrap { padding: 0 calc(var(--u) * 4); }

  .top .wrap {
    flex-wrap: wrap;
    row-gap: calc(var(--u) * 2.5);
    padding-block: calc(var(--u) * 2.5);
    min-height: 0;
  }
  /* The mark stays first, the buttons keep the other end of the same row, and
     the navigation drops to a line of its own where it can scroll. */
  .brand { margin-inline-end: auto; }
  .top-actions { order: 2; flex: 0 0 auto; }
  nav.main {
    order: 3;
    flex: 1 0 100%;
    margin-inline-end: 0;
    overflow-x: auto;
    scrollbar-width: none;
    /* The last item should be able to reach the edge rather than stop under a
       fade of padding. */
    padding-bottom: calc(var(--u) * 0.5);
  }
  nav.main::-webkit-scrollbar { height: 0; }
  nav.main a { white-space: nowrap; }

  /* A table scrolls inside itself, never taking the page with it. `display:
     block` is what makes the box scrollable at all; the header rides with it. */
  table {
    display: block;
    width: 100%;
    max-width: 100%;
    overflow-x: auto;
    -webkit-overflow-scrolling: touch;
  }
  /* Short cells keep their line so the columns stay readable while the table
     scrolls — but a digest is one unbreakable 500-pixel word, and `nowrap` on
     it beats any wrapping rule set elsewhere. It is there to be compared
     character by character, and it can wrap. */
  th, td { white-space: nowrap; }
  th.key, td.key, .key { white-space: normal; word-break: break-all; overflow-wrap: anywhere; }
  /* The one column that must never be the one cut off. */
  td .pill { white-space: nowrap; }

  /* A SHA-256 is sixty-four characters with nothing in it a browser may break
     on, so it is a single 500-pixel word. Outside a table it takes the page
     sideways with it; the digest is there to be compared, and it can wrap. */
  .key { overflow-wrap: anywhere; word-break: break-all; }

  /* A command line is one long unbreakable string too — and unlike a digest it
     is meant to be copied, so it scrolls in its own box rather than wrapping
     into something that cannot be selected as one line. */
  pre, code, .cmd {
    display: block;
    max-width: 100%;
    overflow-x: auto;
    -webkit-overflow-scrolling: touch;
  }

  .figures { gap: calc(var(--u) * 4); }
  .cta { flex-wrap: wrap; }
  .cta .button { flex: 1 1 auto; }
}

/* ═══ the administrator's panel ════════════════════════════════════════
 *
 * The panel had no styles of its own at all: it was written with a sidebar, a
 * main column, blocks, pills and small buttons, and the stylesheet knew about
 * none of them. Every page rendered as a stack of default browser elements —
 * readable, and nothing a person would want to run a service from.
 *
 * The same tokens as the public site, so the two are recognisably one product.
 * Nothing here is decorative: the sidebar keeps the sections one press apart,
 * the blocks separate one subject from the next, and the state colours are the
 * three the application already uses for quality.
 */

.admin-shell {
  display: grid;
  grid-template-columns: 248px minmax(0, 1fr);
  min-height: 100vh;
}

.admin-side {
  border-inline-start: 1px solid var(--border-soft);
  background: var(--sunken);
  display: flex;
  flex-direction: column;
  padding: calc(var(--u) * 7) 0;
  position: sticky;
  top: 0;
  align-self: start;
  max-height: 100vh;
  overflow-y: auto;
}
.admin-side .brand {
  padding: 0 calc(var(--u) * 6) calc(var(--u) * 6);
  font-size: 22px;
}
.admin-side nav { display: flex; flex-direction: column; }
.admin-side nav a {
  padding: calc(var(--u) * 3) calc(var(--u) * 6);
  color: var(--muted);
  font-size: 14px;
  border-inline-start: 2px solid transparent;
  transition: color var(--t-fast) var(--ease), background var(--t-fast) var(--ease);
}
.admin-side nav a:hover { color: var(--fg); background: var(--surface); }
.admin-side nav a.current {
  color: var(--metal);
  background: var(--metal-wash);
  border-inline-start-color: var(--metal);
}

/* Who is signed in, and the way out. Last in the sidebar on a wide screen,
   and beside the brand on a narrow one — as a block of its own it pushed the
   page's title a screenful down on a phone. */
.admin-who {
  margin-top: auto;
  padding: calc(var(--u) * 6);
  border-top: 1px solid var(--border-soft);
  font-size: 12px;
  color: var(--faint);
}
.admin-who form { margin-top: calc(var(--u) * 2); }

.admin-main { padding: calc(var(--u) * 9) calc(var(--u) * 9) calc(var(--u) * 16); min-width: 0; }
.admin-main > h1 { font-size: 26px; margin: 0 0 calc(var(--u) * 6); }

.admin-block {
  background: var(--surface);
  border: 1px solid var(--border-soft);
  border-radius: var(--radius);
  padding: calc(var(--u) * 6);
  margin-bottom: calc(var(--u) * 6);
  overflow-x: auto;
}
.admin-block > h2 {
  font-size: 15px;
  font-weight: 600;
  margin: 0 0 calc(var(--u) * 4);
  display: flex;
  align-items: center;
  gap: calc(var(--u) * 3);
  flex-wrap: wrap;
}
.admin-block table { margin-top: calc(var(--u) * 2); }

/* The panel's forms are plain markup — `<label>`, `<input>`, `<select>` — and
   a browser's own controls are a white box on a dark page. Given the same face
   as the rest without every field having to carry a class. */
.admin-block label { display: block; color: var(--muted); font-size: 13px; margin-bottom: calc(var(--u)); }
.admin-block input:not([type="hidden"]),
.admin-block select {
  font: inherit;
  color: var(--fg);
  background: var(--sunken);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  padding: calc(var(--u) * 2) calc(var(--u) * 3);
  min-height: 38px;
  max-width: 100%;
}
.admin-block input:focus, .admin-block select:focus {
  outline: none;
  border-color: var(--metal);
  box-shadow: 0 0 0 3px var(--metal-soft);
}
.admin-block .inline input:not([type="hidden"]) { min-height: 30px; padding: 2px calc(var(--u) * 2); }
.admin-block .field { margin-top: calc(var(--u) * 4); }

/* Figures: the counts at the top of a page. */
.admin-block .figures { display: flex; flex-wrap: wrap; gap: calc(var(--u) * 8); }
.admin-block .figure { min-width: 120px; }
/* A figure carries its unit on the right of the number — "4.0 GB", not
   "GB 4.0", which is what an RTL paragraph does to it. */
.admin-block .value { font-size: 26px; color: var(--metal); line-height: 1.3; direction: ltr; text-align: start; }
html[dir="rtl"] .admin-block .value { text-align: right; }
.admin-block .value.plain { color: var(--fg); }
.admin-block .label { color: var(--faint); font-size: 13px; }

/* A share of the largest, drawn rather than written. */
.bar-row {
  display: grid;
  grid-template-columns: minmax(90px, 160px) minmax(0, 1fr) 56px;
  align-items: center;
  gap: calc(var(--u) * 3);
  font-size: 14px;
  padding: calc(var(--u) * 1.5) 0;
}
.bar-track { height: 8px; background: var(--raised); border-radius: 999px; overflow: hidden; }
.bar-fill { height: 100%; background: var(--metal); opacity: 0.75; border-radius: 999px; }
.bar-row .count { color: var(--muted); text-align: end; }

/* State, in the three colours the application uses for quality. */
.pill {
  display: inline-block;
  padding: 2px calc(var(--u) * 2.5);
  border-radius: 999px;
  font-size: 12px;
  border: 1px solid var(--border);
  color: var(--muted);
  white-space: nowrap;
}
.pill.ok { color: var(--q-good); border-color: color-mix(in oklch, var(--q-good) 45%, transparent); }
.pill.warn { color: var(--q-fair); border-color: color-mix(in oklch, var(--q-fair) 45%, transparent); }
.pill.bad { color: var(--q-poor); border-color: color-mix(in oklch, var(--q-poor) 45%, transparent); }

/* The small buttons inside table rows. */
.mini {
  font: inherit;
  font-size: 12px;
  padding: calc(var(--u)) calc(var(--u) * 2.5);
  border-radius: var(--radius-xs);
  border: 1px solid var(--border);
  background: var(--raised);
  color: var(--muted);
  cursor: pointer;
  transition: color var(--t-fast) var(--ease), border-color var(--t-fast) var(--ease);
}
.mini:hover { color: var(--fg); border-color: var(--border-lit); }
.mini.danger { color: var(--q-poor); border-color: color-mix(in oklch, var(--q-poor) 40%, transparent); }
.mini.danger:hover { background: color-mix(in oklch, var(--q-poor) 16%, var(--raised)); }
.row-actions { display: flex; flex-wrap: wrap; gap: calc(var(--u) * 1.5); align-items: center; }
.row-actions form, form.inline { display: inline; }
.inline { display: inline-flex; gap: calc(var(--u) * 1.5); align-items: center; }
.inline .input { margin: 0; width: 84px; }

/* What just happened, said once at the top of the page. */
.flash {
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  padding: calc(var(--u) * 3) calc(var(--u) * 4);
  margin-bottom: calc(var(--u) * 5);
  font-size: 14px;
}
.flash.ok { border-color: color-mix(in oklch, var(--q-good) 45%, transparent); background: color-mix(in oklch, var(--q-good) 10%, var(--surface)); }
.flash.error { border-color: color-mix(in oklch, var(--q-poor) 45%, transparent); background: color-mix(in oklch, var(--q-poor) 10%, var(--surface)); }

.grid-2 {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
  gap: 0 calc(var(--u) * 6);
}
.grid-2 .field { margin-top: calc(var(--u) * 4); }

/* Signing in: one card on an empty page, like the application's own. */
.login-shell {
  min-height: 100vh;
  display: grid;
  place-items: center;
  padding: calc(var(--u) * 6);
}
.login-card {
  width: min(400px, 100%);
  background: var(--surface);
  border: 1px solid var(--border-soft);
  border-radius: var(--radius);
  padding: calc(var(--u) * 8);
}

@media (max-width: 860px) {
  /* The sidebar becomes a strip along the top: a 248-pixel column on a phone
     leaves the table nothing to be read in. */
  /* Two rows, and only the second one grows. Left to `auto` both, the shell's
     `min-height: 100vh` is shared between them and the strip at the top pushes
     the page's title a third of the way down a phone. */
  .admin-shell { grid-template-columns: minmax(0, 1fr); grid-template-rows: auto 1fr; }
  .admin-side {
    position: static;
    max-height: none;
    border-inline-start: 0;
    border-bottom: 1px solid var(--border-soft);
    padding: calc(var(--u) * 4) 0;
  }
  .admin-side {
    display: grid;
    grid-template-columns: auto minmax(0, 1fr);
    grid-template-areas: "brand who" "nav nav";
    align-items: center;
  }
  .admin-side .brand { grid-area: brand; padding: 0 calc(var(--u) * 5) calc(var(--u) * 2); }
  .admin-side nav { grid-area: nav; }
  .admin-who {
    grid-area: who;
    margin: 0;
    padding: 0 calc(var(--u) * 5);
    border-top: 0;
    text-align: end;
  }
  .admin-who form { display: inline; margin: 0 calc(var(--u) * 2) 0 0; }
  .admin-side nav { flex-direction: row; overflow-x: auto; padding: 0 calc(var(--u) * 3); }
  .admin-side nav a { white-space: nowrap; border-inline-start: 0; border-bottom: 2px solid transparent; }
  .admin-side nav a.current { border-inline-start: 0; border-bottom-color: var(--metal); }
  .admin-main { padding: calc(var(--u) * 6) calc(var(--u) * 4) calc(var(--u) * 12); }
  .bar-row { grid-template-columns: minmax(70px, 1fr) minmax(0, 2fr) 44px; }
}

/* The list of titles that actually work, on the home page. A bare list of two
   names, given room to be read rather than bulleted like a footnote. */
.plain-list {
  list-style: none;
  padding: 0;
  margin: calc(var(--u) * 5) 0 calc(var(--u) * 4);
  display: flex;
  flex-wrap: wrap;
  gap: calc(var(--u) * 3);
}
.plain-list li {
  border: 1px solid var(--metal-line);
  background: var(--metal-wash);
  border-radius: 999px;
  padding: calc(var(--u) * 2) calc(var(--u) * 5);
  font-size: 15px;
}

/* A field with a button inside it: the password and its reveal. */
.input-with-button { position: relative; display: flex; }
.input-with-button .input { padding-inline-end: calc(var(--u) * 16); }
/* `.pass-toggle`, not `.reveal`.
 *
 * `.reveal` was already taken: it is the class every section on this site
 * carries for the scroll-in animation. Giving the password button the same name
 * made every one of those sections `position: absolute; top: 50%` — the account
 * page's five blocks stacked on top of each other in a 356px box, unreadable,
 * with the request form covering the password controls. It was site-wide, and
 * it shipped. */
.pass-toggle {
  position: absolute;
  inset-inline-end: calc(var(--u) * 2);
  top: 50%;
  transform: translateY(-50%);
  font: inherit; font-size: 12px;
  color: var(--muted);
  background: var(--raised);
  border: 1px solid var(--border);
  border-radius: var(--radius-xs);
  padding: calc(var(--u)) calc(var(--u) * 2);
  cursor: pointer;
  min-height: 28px;
}
.pass-toggle:hover { color: var(--fg); border-color: var(--border-lit); }
.pass-toggle:focus-visible { outline: 2px solid var(--metal); outline-offset: 2px; }

/* The sign-in card sat below the fold on a 712px-tall window: a form whose
   submit button cannot be seen without scrolling reads as a form that is still
   loading. The whole card is sized against the viewport now — the air above it,
   the padding inside it and the gaps between its fields all shrink together on
   a short window and go back to the designed spacing on a tall one. */
.card {
  margin-block: clamp(calc(var(--u) * 4), 4vh, calc(var(--u) * 20));
  padding: clamp(calc(var(--u) * 5), 4.4vh, calc(var(--u) * 9));
}
.card .field { margin-top: clamp(calc(var(--u) * 3), 2.6vh, calc(var(--u) * 5)); }
.card h1 { margin-block: 0 calc(var(--u) * 2); font-size: clamp(20px, 2.9vh, 26px); }
.card .tabs { margin-top: clamp(calc(var(--u) * 2), 2vh, calc(var(--u) * 5)); }
.card .note { margin-block: calc(var(--u)) 0; }
.card .metal-rule { margin-bottom: clamp(calc(var(--u) * 2), 1.6vh, calc(var(--u) * 4)); }
.card .button { margin-top: clamp(calc(var(--u) * 4), 3vh, calc(var(--u) * 7)); }
.card .card-foot { margin-top: clamp(calc(var(--u) * 3), 2vh, calc(var(--u) * 5)); }

/* A section is 88px of padding top and bottom, which on a short window is the
   difference between seeing the submit button and not. The card's own section
   gives it up; every other section keeps it. */
section.card-section { padding-block: clamp(calc(var(--u) * 2), 3vh, calc(var(--u) * 22)); }

/* A laptop at 1280x712 — the size a tester found the submit button below the
   fold at — has 88 fewer pixels than the design assumes. The bar at the top
   gives some of them back; it is navigation, and on this page the form is what
   the visitor came for. */
@media (max-height: 780px) {
  .top { padding-block: calc(var(--u) * 2); }
  .card-section .card { margin-block: calc(var(--u) * 2); }
}

/* A credential behind a press. The subscription token is what a client shows to
   fetch an account's configuration, and it was printed in full on a page that
   lists every subscriber. */
.secret { margin-top: 4px; }
.secret > summary {
  cursor: pointer; font-size: 12px; color: var(--faint);
  list-style: none; padding: 2px 0;
}
.secret > summary::-webkit-details-marker { display: none; }
.secret > summary::before { content: "•••• "; letter-spacing: 2px; }
.secret[open] > summary { color: var(--muted); }
.secret code { display: block; margin-top: 4px; user-select: all; }

/* Controls in a table row: large enough to hit, with a border that can be seen.
   They were 12px text on a 1px hairline. */
.mini {
  font-size: 13px;
  padding: calc(var(--u) * 1.5) calc(var(--u) * 3);
  min-height: 32px;
  color: var(--fg);
  border-color: var(--border-lit);
}
.mini:hover { background: var(--hover); }
.row-actions { gap: calc(var(--u) * 2); }

/* What the subscriber asked, and the answer being written back. */
.ticket-text { max-width: 42ch; white-space: normal; line-height: 1.9; }
.ticket-reply { min-width: 34ch; }
.ticket-reply textarea { width: 100%; font-family: var(--font-fa); line-height: 1.8; }
