/*
 * The console's only stylesheet. Hand-written, on purpose.
 *
 * Tailwind is deferred rather than rejected. M1 has four pages; adopting
 * Tailwind now would mean a Node build stage, a package.json and a lockfile, a
 * digest-pinned Node image, new make targets, and supply-chain gates on
 * everything that brings — a large addition to the milestone the build plan
 * already calls the hardest. And the "retrofitting is expensive" argument that
 * forced the Content-Security-Policy and the translations into this same change
 * does not apply here: converting four templates to utility classes later is an
 * hour's work, because nothing else depends on how they are styled.
 *
 * So this file follows Tailwind's token discipline without Tailwind, and the
 * later conversion is mechanical:
 *
 *   - Two layers of custom property. The palette holds raw values; the roles
 *     below it hold meaning. Components only ever reference a role, so dark mode
 *     is one block of overrides rather than a rule per component — the same
 *     shape as @theme and @theme inline.
 *   - A spacing scale and a type scale, with nothing off them. A one-off value
 *     used more than twice is a missing token, here as there.
 *   - :focus-visible everywhere, never a removed outline.
 *   - Contrast checked as a pair, not as a colour: the muted text on the sunk
 *     surface is the combination that usually fails, so it is the one stated.
 *
 * Dark mode follows the operating system. There is no theme switch, because a
 * switch needs somewhere to remember the choice and that is a preference model
 * for one operator with four pages.
 */

/* --- Palette: raw values, referenced only by the roles below ------------- */

:root {
  --slate-50: #f8fafc;
  --slate-100: #f1f5f9;
  --slate-200: #e2e8f0;
  --slate-400: #94a3b8;
  --slate-500: #64748b;
  --slate-600: #475569;
  --slate-700: #334155;
  --slate-800: #1e293b;
  --slate-900: #0f172a;
  --slate-950: #020617;

  --teal-500: #0d9488;
  --teal-600: #0f766e;
  --teal-300: #5eead4;

  --green-600: #15803d;
  --green-400: #4ade80;

  --amber-600: #b45309;
  --amber-400: #fbbf24;

  --red-600: #b91c1c;
  --red-400: #f87171;

  /* --- Spacing scale. Everything below uses these and nothing else. ------ */
  --space-1: 0.25rem;
  --space-2: 0.5rem;
  --space-3: 0.75rem;
  --space-4: 1rem;
  --space-6: 1.5rem;
  --space-8: 2rem;
  --space-12: 3rem;

  /* --- Type scale -------------------------------------------------------- */
  --text-xs: 0.75rem;
  --text-sm: 0.875rem;
  --text-base: 1rem;
  --text-lg: 1.125rem;
  --text-2xl: 1.5rem;

  --leading-tight: 1.25;
  --leading-normal: 1.6;

  --radius-sm: 0.25rem;
  --radius: 0.5rem;

  /* System fonts only. A web font is a file to ship, a directive to widen in
     the Content-Security-Policy, and a layout shift, for four pages of text. */
  --font-sans:
    system-ui, -apple-system, "Segoe UI", roboto, "Helvetica Neue", arial, sans-serif;
  --font-mono: ui-monospace, "SFMono-Regular", "Cascadia Mono", "Liberation Mono", menlo,
    monospace;

  /* --- Roles: what the rules below actually reference -------------------- */
  --surface: var(--slate-50);
  --surface-raised: #ffffff;
  --surface-sunk: var(--slate-100);
  --border: var(--slate-200);
  --border-strong: var(--slate-400);

  --ink: var(--slate-900);
  --ink-muted: var(--slate-600); /* 7.0:1 on --surface-sunk */
  --ink-inverse: var(--slate-50);

  --accent: var(--teal-600);
  --accent-ink: #ffffff;

  --ok: var(--green-600);
  --warn: var(--amber-600);
  --danger: var(--red-600);

  --focus-ring: var(--teal-600);
}

@media (prefers-color-scheme: dark) {
  :root {
    /* Only the roles change. Not an inversion: the raised surface is lighter
       than the page rather than darker, because in the dark a shadow conveys
       nothing and elevation has to come from the surface itself. */
    --surface: var(--slate-950);
    --surface-raised: var(--slate-900);
    --surface-sunk: var(--slate-800);
    --border: var(--slate-700);
    --border-strong: var(--slate-500);

    --ink: var(--slate-100);
    --ink-muted: var(--slate-400); /* 6.4:1 on --surface-sunk */
    --ink-inverse: var(--slate-950);

    --accent: var(--teal-500);
    --accent-ink: var(--slate-950);

    --ok: var(--green-400);
    --warn: var(--amber-400);
    --danger: var(--red-400);

    --focus-ring: var(--teal-300);
  }
}

/* --- Base ---------------------------------------------------------------- */

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

html {
  color-scheme: light dark;
}

body {
  margin: 0;
  background: var(--surface);
  color: var(--ink);
  font-family: var(--font-sans);
  font-size: var(--text-base);
  line-height: var(--leading-normal);
}

h1,
h2 {
  line-height: var(--leading-tight);
  margin: 0 0 var(--space-4);
}

h1 {
  font-size: var(--text-2xl);
}

h2 {
  font-size: var(--text-lg);
}

p {
  margin: 0 0 var(--space-4);
  max-width: 65ch;
}

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

/* Never removed, only replaced. :focus-visible rather than :focus so the ring
   appears for a keyboard user without flashing on every mouse click. */
:focus-visible {
  outline: 2px solid var(--focus-ring);
  outline-offset: 2px;
  border-radius: var(--radius-sm);
}

/* --- Layout -------------------------------------------------------------- */

.masthead {
  background: var(--surface-raised);
  border-bottom: 1px solid var(--border);
}

.masthead__inner {
  align-items: center;
  display: flex;
  gap: var(--space-4);
  margin: 0 auto;
  max-width: 60rem;
  padding: var(--space-3) var(--space-4);
}

.masthead__name {
  color: var(--ink);
  font-weight: 600;
  text-decoration: none;
}

.masthead__spacer {
  flex: 1;
}

.page {
  margin: 0 auto;
  max-width: 60rem;
  padding: var(--space-8) var(--space-4) var(--space-12);
}

/* The narrow column the three authentication pages sit in. */
.page--narrow {
  max-width: 28rem;
}

/* --- Cards and forms ----------------------------------------------------- */

.card {
  background: var(--surface-raised);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: var(--space-6);
}

.field {
  margin-bottom: var(--space-4);
}

.field label {
  display: block;
  font-size: var(--text-sm);
  font-weight: 600;
  margin-bottom: var(--space-1);
}

.field input {
  background: var(--surface);
  border: 1px solid var(--border-strong);
  border-radius: var(--radius-sm);
  color: var(--ink);
  font: inherit;
  padding: var(--space-2) var(--space-3);
  width: 100%;
}

.field .helptext {
  color: var(--ink-muted);
  display: block;
  font-size: var(--text-sm);
  margin-top: var(--space-1);
}

.errorlist {
  color: var(--danger);
  font-size: var(--text-sm);
  list-style: none;
  margin: 0 0 var(--space-3);
  padding: 0;
}

/* Two buttons side by side, wrapping rather than shrinking below the pointer
   target size on a narrow screen. */
.actions {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-3);
}

/* --- Fact lists ---------------------------------------------------------- */

/* The context on the approval page: what the machine says it is, so an operator
   can notice a request that is not the one they just started. A grid rather
   than a table because it is one record, not a set of them — and it collapses
   to stacked rows on a narrow screen, where a two-column table would not. */
.facts {
  display: grid;
  gap: var(--space-1) var(--space-4);
  grid-template-columns: 1fr;
  margin: 0 0 var(--space-6);
}

.facts dt {
  color: var(--ink-muted);
  font-size: var(--text-xs);
  font-weight: 600;
  letter-spacing: 0.04em;
  text-transform: uppercase;
}

.facts dd {
  margin: 0 0 var(--space-3);
}

@media (min-width: 30rem) {
  .facts {
    /* The label column is sized to the longest label, and the value column
       takes the rest. */
    grid-template-columns: auto 1fr;
  }

  .facts dt {
    /* Optical alignment with the value beside it, which sits on the base type
       scale rather than the extra-small one. */
    padding-top: 0.2rem;
  }
}

/* --- Buttons ------------------------------------------------------------- */

.button {
  background: var(--accent);
  border: 1px solid transparent;
  border-radius: var(--radius-sm);
  color: var(--accent-ink);
  cursor: pointer;
  display: inline-block;
  font: inherit;
  font-weight: 600;
  /* Comfortably past the 24x24 minimum for a pointer target, and past 44px tall
     on touch once the line box is counted. */
  min-height: 2.75rem;
  padding: var(--space-2) var(--space-4);
  text-decoration: none;
}

.button--quiet {
  background: transparent;
  border-color: var(--border-strong);
  color: var(--ink);
}

.button--danger {
  background: transparent;
  border-color: var(--danger);
  color: var(--danger);
}

/* --- Messages ------------------------------------------------------------ */

.messages {
  list-style: none;
  margin: 0 0 var(--space-6);
  padding: 0;
}

.messages li {
  background: var(--surface-sunk);
  border-left: 4px solid var(--border-strong);
  border-radius: var(--radius-sm);
  margin-bottom: var(--space-2);
  padding: var(--space-3) var(--space-4);
}

.messages li.success {
  border-left-color: var(--ok);
}

.messages li.error {
  border-left-color: var(--danger);
}

/* --- Status ------------------------------------------------------------- */

/* Colour is never the only signal: every dot is followed by its own word, so
   the state is readable in greyscale and by a screen reader. */
.status {
  align-items: center;
  display: inline-flex;
  gap: var(--space-2);
  white-space: nowrap;
}

.status::before {
  border-radius: 50%;
  content: "";
  height: 0.625rem;
  width: 0.625rem;
}

.status--online::before {
  background: var(--ok);
}

.status--offline::before {
  background: var(--ink-muted);
}

.status--revoked::before {
  background: var(--danger);
}

/* --- Tables -------------------------------------------------------------- */

/* Wide content scrolls inside its own box; the page body never scrolls
   sideways. A fingerprint is 50 characters and there is no narrower spelling. */
.scroller {
  overflow-x: auto;
}

.machines {
  border-collapse: collapse;
  min-width: 40rem;
  width: 100%;
}

.machines th,
.machines td {
  border-bottom: 1px solid var(--border);
  padding: var(--space-3) var(--space-4) var(--space-3) 0;
  text-align: left;
  vertical-align: top;
}

.machines th {
  color: var(--ink-muted);
  font-size: var(--text-xs);
  font-weight: 600;
  letter-spacing: 0.04em;
  text-transform: uppercase;
}

.machines .name {
  font-weight: 600;
}

/* --- Monospace blocks ---------------------------------------------------- */

.secret,
.fingerprint,
.codes li {
  font-family: var(--font-mono);
  font-size: var(--text-sm);
  word-break: break-all;
}

.secret {
  background: var(--surface-sunk);
  border-radius: var(--radius-sm);
  display: block;
  font-size: var(--text-lg);
  letter-spacing: 0.08em;
  margin-bottom: var(--space-4);
  padding: var(--space-3) var(--space-4);
  user-select: all;
}

.codes {
  columns: 2;
  gap: var(--space-4);
  list-style: none;
  margin: 0 0 var(--space-6);
  padding: 0;
}

.codes li {
  background: var(--surface-sunk);
  border-radius: var(--radius-sm);
  margin-bottom: var(--space-2);
  padding: var(--space-2) var(--space-3);
}

.muted {
  color: var(--ink-muted);
  font-size: var(--text-sm);
}

/* Something the operator must read before acting, but which is not an error and
   does not block the form — today, a fingerprint that already belongs to a
   machine nobody has revoked. Deliberately not .muted: this is the opposite of
   supporting detail, and the left rule is what stops it reading as another
   paragraph on a page that already has several. Colour is not the only signal —
   the border and the indent carry it too, for anyone who cannot see the amber. */
.warning {
  border-left: 3px solid var(--warn);
  color: var(--ink);
  font-size: var(--text-sm);
  padding: var(--space-2) 0 var(--space-2) var(--space-3);
}

.empty {
  color: var(--ink-muted);
  padding: var(--space-8) 0;
  text-align: center;
}

/* Announced, never shown. The actions column has no visible heading — a header
   cell reading "Actions" above one button is noise — but a screen reader
   navigating the table by column needs the name. */
.sr-only {
  border: 0;
  clip-path: inset(50%);
  height: 1px;
  overflow: hidden;
  position: absolute;
  white-space: nowrap;
  width: 1px;
}
