
/* ====================================================================== *
 * The door's tokens are the panel's tokens. Every triplet below is the
 * value admin-panel.mjs measured and argued for; nothing was re-picked
 * for this page. Colours are RGB TRIPLETS rather than hex because a
 * border wants a fraction of the colour its text is set in, and there is
 * nothing to take a percentage of in a hex literal.
 * ====================================================================== */
:root {
  color-scheme: light dark;

  --p-sheet: 251, 252, 253;
  --p-sunk: 241, 244, 247;
  --p-raised: 255, 255, 255;

  --p-ink: 14, 26, 32;
  --p-ink-2: 74, 96, 105;
  --p-ink-3: 88, 108, 122;

  --p-rule: 213, 221, 229;
  --p-rule-strong: 185, 198, 208;
  /* A rule separates content and may be faint; an EDGE is the only thing
     saying where a control is, and WCAG 1.4.11 asks 3:1 of it. */
  --p-edge: 117, 138, 151;

  --p-mark: 0, 101, 92;
  --p-mark-line: 14, 140, 126;
  --p-alert: 166, 35, 26;
}

@media (prefers-color-scheme: dark) {
  :root:not([data-theme='light']) {
  --p-sheet: 11, 17, 22;
  --p-sunk: 7, 12, 16;
  --p-raised: 19, 28, 35;
  --p-ink: 221, 231, 234;
  --p-ink-2: 147, 167, 176;
  --p-ink-3: 122, 144, 155;
  --p-rule: 29, 41, 51;
  --p-rule-strong: 49, 66, 78;
  --p-edge: 87, 112, 127;
  --p-mark: 62, 220, 192;
  --p-mark-line: 47, 185, 162;
  --p-alert: 255, 124, 114;
}
}
:root[data-theme='dark'] {
  --p-sheet: 11, 17, 22;
  --p-sunk: 7, 12, 16;
  --p-raised: 19, 28, 35;
  --p-ink: 221, 231, 234;
  --p-ink-2: 147, 167, 176;
  --p-ink-3: 122, 144, 155;
  --p-rule: 29, 41, 51;
  --p-rule-strong: 49, 66, 78;
  --p-edge: 87, 112, 127;
  --p-mark: 62, 220, 192;
  --p-mark-line: 47, 185, 162;
  --p-alert: 255, 124, 114;
}

/* An explicit choice has to reach the parts of the page this stylesheet
   does not draw: the scrollbar, the caret, the form controls the
   operating system paints for itself. Without these a page set to dark
   keeps a white scrollbar on a machine set to light. */
:root[data-theme='light'] { color-scheme: light; }
:root[data-theme='dark'] { color-scheme: dark; }

:root {
  --surface: rgb(var(--p-sheet));
  --surface-sunk: rgb(var(--p-sunk));
  --surface-raised: rgb(var(--p-raised));
  --ink: rgb(var(--p-ink));
  --ink-2: rgb(var(--p-ink-2));
  --ink-3: rgb(var(--p-ink-3));
  --rule: rgb(var(--p-rule));
  --rule-strong: rgb(var(--p-rule-strong));
  --edge: rgb(var(--p-edge));
  /* --mark WRITES, --mark-line DRAWS. The line colour measures 4.03:1,
     which clears the 3:1 a 2px bar needs and does not clear the 4.5:1 a
     word needs. If you want this colour on a label, you want --mark. */
  --mark: rgb(var(--p-mark));
  --mark-line: rgb(var(--p-mark-line));
  --alert: rgb(var(--p-alert));

  /* Solid values, not opacity: opacity on a filled button composites its
     label to an illegible grey and fades the border that said it was a
     control at all. */
  --off-face: var(--surface-sunk);
  --off-ink: var(--ink-3);
  --off-edge: var(--rule);
  --wash: rgba(var(--p-mark-line), 0.18);

  --ui: ui-sans-serif, -apple-system, BlinkMacSystemFont, "Segoe UI Variable Text",
        "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
  --data: ui-monospace, "SF Mono", SFMono-Regular, "Cascadia Mono", "Segoe UI Mono",
          "Roboto Mono", Menlo, Consolas, monospace;

  --t-micro: 0.6875rem;
  --t-small: 0.75rem;
  --t-base: 0.8125rem;
  --t-mid: 0.875rem;
  --t-figure: 1.0625rem;
  --t-large: 1.5rem;

  --w-body: 400;
  --w-data: 500;
  --w-head: 600;

  --track-caps: 0.07em;

  --s-1: 4px;  --s-2: 8px;  --s-3: 12px; --s-4: 16px;
  --s-5: 24px; --s-6: 32px; --s-7: 48px;

  --hair: 1px;
  --pin: 2px;
  --radius: 3px;
  --control-h: 42px;
  --sheet-w: 27rem;
  --box-h: 58px;

  /* Two durations. The 90ms is the panel's whole motion budget for a
     colour change; the 180ms exists only for the step change and is
     argued for where it is used. */
  --dur-tap: 90ms;
  --dur-step: 180ms;
  --ease: cubic-bezier(0.2, 0.8, 0.3, 1);

  accent-color: var(--mark);
  caret-color: var(--mark-line);
}

* { box-sizing: border-box; }
[hidden] { display: none !important; }
::selection { background: var(--wash); color: var(--ink); }

/* The ground is the SUNK surface and the form sits on the sheet — one
   step in colour plus one hairline, which is the whole elevation ladder
   this design has. It is not a card: no shadow, no 10px radius, no
   padding standing in for a border. */
body.door {
  margin: 0;
  min-height: 100vh;
  min-height: 100dvh;
  display: grid;
  grid-template-rows: auto minmax(0, 1fr);
  background: var(--surface-sunk);
  color: var(--ink);
  font-family: var(--ui);
  font-size: var(--t-base);
  font-weight: var(--w-body);
  line-height: 1.45;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
}

:where(a, button, input, summary):focus-visible {
  outline: var(--pin) solid var(--mark-line);
  outline-offset: 2px;
}

/* ====================================================================== *
 * THE TOP STRIP
 * ====================================================================== */
.bar {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--s-3);
  padding: var(--s-3) var(--s-5);
  border-bottom: var(--hair) solid var(--rule-strong);
}
.bar__mark { display: grid; gap: 2px; min-width: 0; }
/* 14px. The wordmark is a stamp on a working document; a login page whose
   logo outweighs its own heading is a marketing page. */
.bar__wordmark { font-size: var(--t-mid); font-weight: var(--w-head); color: var(--ink); }
.bar__sub {
  font-size: var(--t-micro); letter-spacing: var(--track-caps);
  text-transform: uppercase; color: var(--ink-3);
}
/* margin-left rather than justify-content: when the strip wraps at a
   narrow width the tools keep their own right edge instead of centring
   themselves under the wordmark. */
.bar__tools { display: flex; flex-wrap: wrap; gap: var(--s-2); margin-left: auto; }

/* ---- the two switches. One shape, two jobs. ---- */
.switch {
  display: inline-flex;
  background: var(--surface);
  border: var(--hair) solid var(--edge);
  border-radius: var(--radius);
  overflow: hidden;
}
.switch__opt {
  appearance: none;
  min-height: 26px;
  padding: 0 10px;
  border: 0;
  background-color: transparent;
  color: var(--ink-3);
  font-family: var(--ui);
  font-size: var(--t-micro);
  font-weight: var(--w-head);
  letter-spacing: var(--track-caps);
  line-height: 1;
  cursor: pointer;
  transition: background-color var(--dur-tap) var(--ease), color var(--dur-tap) var(--ease);
}
.switch__opt + .switch__opt { border-left: var(--hair) solid var(--rule); }
.switch__opt:hover { background-color: var(--surface-raised); color: var(--ink); }
/* The chosen segment carries the same 2px mark the panel's rail spends on
   the current section, drawn as a background rather than a border so the
   button does not change height, and as a gradient rather than a shadow
   because this design has none. */
.switch__opt[aria-pressed='true'] {
  color: var(--ink);
  background-color: var(--surface-raised);
  background-image: linear-gradient(to top, var(--mark-line) 2px, transparent 2px);
}
/* Inset, or the ring is clipped by the group's own overflow. */
.switch__opt:focus-visible { outline-offset: -2px; }
/* A language is a word, not a code, so it never takes the uppercase and
   the tracking the theme labels take. */
#lang .switch__opt { text-transform: none; letter-spacing: 0; font-size: var(--t-small); }

/* ====================================================================== *
 * THE SHEET
 * ====================================================================== */
.stage { display: grid; place-items: center; padding: var(--s-6) var(--s-5) var(--s-7); }
.sheet {
  width: 100%;
  max-width: var(--sheet-w);
  padding: var(--s-5) var(--s-5) var(--s-4);
  background: var(--surface);
  border: var(--hair) solid var(--rule-strong);
  border-radius: var(--radius);
}
.figure { display: block; width: 100%; height: auto; color: var(--rule-strong); }

/* A pane is one step of the door. Only ever one is on screen.
   The panel behind this door spends its whole motion budget on one 90ms
   colour change and forbids animating a layout property. The door is
   allowed one more: the two steps are the same rectangle showing
   different content, and without a movement the change reads as a page
   reload rather than as going forward. 180ms, opacity and transform only,
   and nothing on the page reflows while it runs. */
.pane {
  padding-top: var(--s-4);
  transition: opacity var(--dur-step) var(--ease), transform var(--dur-step) var(--ease);
}
.pane--arriving { opacity: 0; transform: translateY(6px); }
@media (prefers-reduced-motion: reduce) {
  .pane { transition: none; }
  .pane--arriving { opacity: 1; transform: none; }
}

/*
 * The heading block is centred and the form below it is not, which is deliberate rather than
 * inconsistent. A title and its one line are read as a unit and the eye wants them on the card's
 * axis; a column of labelled inputs is read down its left edge, and centring those would move the
 * start of every label to a different place.
 */
.pane__head {
  display: flex; align-items: center; justify-content: center; gap: var(--s-3);
  margin-bottom: var(--s-4);
}
.pane > h1, .pane > .lede { text-align: center; }
.pane > .lede { margin-inline: auto; }
/* Mono, because it is a count. */
.pane__step {
  margin: 0; font-family: var(--data); font-size: var(--t-micro); font-weight: var(--w-data);
  letter-spacing: var(--track-caps); text-transform: uppercase; color: var(--ink-3);
  font-variant-numeric: tabular-nums;
}
.rung { display: flex; gap: var(--s-1); }
.rung__seg { display: block; width: 18px; height: var(--pin); background: var(--rule-strong); }
.rung__seg--on { background: var(--mark-line); }

h1 {
  margin: 0; font-size: var(--t-large); font-weight: var(--w-head); line-height: 1.2;
  letter-spacing: -0.012em; color: var(--ink); overflow-wrap: break-word;
}
.lede { margin: var(--s-2) 0 0; color: var(--ink-2); max-width: 40ch; }
.hint { display: block; margin: var(--s-1) 0 0; color: var(--ink-3); font-size: var(--t-small); }

/* ====================================================================== *
 * THE FORM
 * ====================================================================== */
form { display: grid; gap: var(--s-4); margin: var(--s-5) 0 0; }
.field { display: grid; gap: var(--s-1); margin: 0; min-width: 0; }
label {
  font-size: var(--t-micro); font-weight: var(--w-head); line-height: 1.3;
  letter-spacing: var(--track-caps); text-transform: uppercase; color: var(--ink-3);
}
/* The enrolment page's code label is a sentence, not a column head: at
   11px uppercase and tracked it would be a wall. */
.label { font-size: var(--t-small); line-height: 1.4; color: var(--ink-2); }

input[type='text'], input[type='password'] {
  width: 100%;
  min-height: var(--control-h);
  /* Enough padding that a full-width box does not read as a slot. The height token is shared with
     the button below, so the three controls in this form stay one family however it is tuned. */
  padding: 10px 12px;
  font-family: var(--ui); font-size: var(--t-base); font-weight: var(--w-body);
  line-height: 1.25; color: var(--ink);
  background-color: var(--surface);
  border: var(--hair) solid var(--edge);
  border-radius: var(--radius);
  appearance: none;
  transition: background-color var(--dur-tap) var(--ease), border-color var(--dur-tap) var(--ease);
}
/* A login name is a machine identifier and a password is a row of dots
   that has to be countable, so both go to the mono face by the same rule
   the panel applies to every machine-format value. */
input.ident { font-family: var(--data); font-weight: var(--w-data); }
input:hover:not(:focus) { border-color: var(--ink-3); }
/* A box that cannot be right says so on its edge. Colour only, no icon and no shake: this is a
   correction, not an alarm. */
input.is-wrong, input.is-wrong:hover:not(:focus) { border-color: var(--alert); }
/* The lift is on :focus and the ring on :focus-visible, deliberately:
   browsers disagree about whether a clicked field matches :focus-visible
   and do not disagree about :focus, so the field being typed in is always
   the one lit thing on the screen. */
input:focus {
  background-color: var(--surface-raised); border-color: var(--mark-line); outline: none;
}
input:disabled {
  color: var(--off-ink); background-color: var(--off-face); border-color: var(--off-edge);
  cursor: not-allowed;
}

/* ---- the six boxes ----
   The most-looked-at control on the screen, so it is drawn the way the
   panel draws a figure: mono, tabular, one weight up. The wider gap
   before the fourth box is the grouping every six-digit code is read in,
   and it costs nothing but eight pixels. */
.otp { display: flex; gap: var(--s-2); }
/* input.otp__box, not .otp__box. THE BOXES SHIPPED AS PLAIN 34px TEXT
   FIELDS the first time this was written, and nothing in the rule below
   looked wrong: input[type='text'] is one attribute plus one element and
   outranks a bare class, so every line here that the generic field also
   sets -- height, font, size, padding -- lost silently and the code was
   drawn in 13px sans. Measured, not noticed: a screenshot of six
   rectangles with nothing in them looks the same either way. */
input.otp__box {
  flex: 1 1 0;
  min-width: 0;
  min-height: var(--box-h);
  /* Zero, on purpose: a centred single glyph in a box with side padding
     is centred in what is left of the box, not in the box. */
  padding: 0;
  text-align: center;
  font-family: var(--data); font-size: var(--t-large); font-weight: var(--w-data);
  font-variant-numeric: tabular-nums;
  color: var(--ink);
  background-color: var(--surface);
  border: var(--hair) solid var(--edge);
  border-radius: var(--radius);
  appearance: none;
  transition: background-color var(--dur-tap) var(--ease), border-color var(--dur-tap) var(--ease);
}
input.otp__box:nth-child(4) { margin-left: var(--s-2); }
input.otp__box:hover:not(:focus) { border-color: var(--ink-3); }
input.otp__box:focus {
  background-color: var(--surface-raised); border-color: var(--mark-line); outline: none;
}
input.otp__box:disabled {
  color: var(--off-ink); background-color: var(--off-face); border-color: var(--off-edge);
}

/* ---- the name the code is being typed for ---- */
.whoami {
  display: flex; flex-wrap: wrap; align-items: baseline; gap: var(--s-2);
  margin: var(--s-4) 0 0; padding: var(--s-2) var(--s-3);
  background: var(--surface-sunk); border-left: var(--pin) solid var(--mark-line);
}
.whoami__key {
  font-size: var(--t-micro); font-weight: var(--w-head); letter-spacing: var(--track-caps);
  text-transform: uppercase; color: var(--ink-3);
}
.whoami__value {
  font-family: var(--data); font-weight: var(--w-data); color: var(--ink);
  overflow-wrap: anywhere; min-width: 0;
}

/* ---- buttons ---- */
.foot { display: flex; flex-wrap: wrap; align-items: center; gap: var(--s-2); margin: 0; }
button.action {
  display: inline-flex; align-items: center; justify-content: center;
  min-height: var(--control-h); padding: 0 var(--s-4);
  font-family: var(--ui); font-size: var(--t-base); font-weight: var(--w-head);
  line-height: 1.25; white-space: nowrap;
  border: var(--hair) solid transparent; border-radius: var(--radius);
  cursor: pointer;
  transition: background-color var(--dur-tap) var(--ease),
              border-color var(--dur-tap) var(--ease), color var(--dur-tap) var(--ease);
  /* The one filled block on the page, and it is ink rather than an
     accent: 17.2:1 light, 15.1:1 dark. */
  color: var(--surface); background-color: var(--ink); border-color: var(--ink);
}
/* On the first step Continue is the only thing to do, so it takes the
   width; on the second it shares the row with Back. */
#login button.action, #claim button.action { width: 100%; }
button.action:hover { background-color: var(--ink-2); border-color: var(--ink-2); }
/* Quiet: an outline in the control edge, so the secondary action is the
   same object as an input rather than a third kind of box. */
button.action--quiet {
  color: var(--ink-2); background-color: transparent; border-color: var(--edge);
}
button.action--quiet:hover {
  color: var(--ink); background-color: var(--surface-raised); border-color: var(--ink-3);
}
button.action:disabled {
  color: var(--off-ink); background-color: var(--off-face); border-color: var(--off-edge);
  cursor: not-allowed;
}
/* WORKING, not broken. Between pressing Sign in and the answer arriving
   there is a round trip, and a button that only greys out reads as a dead
   control. Same painted 2px bar the panel uses, and it outranks :disabled
   on purpose: the handler sets both together. */
button.action[aria-busy='true'], button.action[aria-busy='true']:disabled {
  color: var(--surface); background-color: var(--ink); border-color: var(--ink);
  background-image: linear-gradient(to top, var(--mark-line) 2px, transparent 2px);
  cursor: progress;
}

/* ---- the one thing on this page that is allowed to be red ---- */
.message {
  margin: var(--s-4) 0 0; padding-left: var(--s-2);
  border-left: var(--pin) solid var(--alert);
  color: var(--alert); font-size: var(--t-base); line-height: 1.4;
}
/* Without this the empty node still collects the margin above it and
   every step carries sixteen pixels of dead air under it forever. */
.message:empty { display: none; }

/* ====================================================================== *
 * ENROLMENT
 * ====================================================================== */
.steps {
  margin: var(--s-4) 0 0; padding-left: 20px; display: grid; gap: var(--s-2);
  color: var(--ink-2);
}
.steps li { line-height: 1.45; }
/* The square keeps a white plate in both themes: a QR code is read by a
   camera that expects dark modules on a light ground, and inverting it is
   the one change that stops a phone recognising it. */
.qr {
  width: min(232px, 62vw); margin: var(--s-4) 0 0; padding: 10px;
  background: #ffffff; border: var(--hair) solid var(--rule-strong); border-radius: var(--radius);
}
.qr svg { display: block; width: 100%; height: auto; }
.fallback { margin: var(--s-4) 0 0; color: var(--ink-2); font-size: var(--t-small); }
.fallback summary { cursor: pointer; color: var(--ink-2); }
.key {
  font-family: var(--data); font-size: var(--t-figure); font-weight: var(--w-data);
  letter-spacing: 0.08em; overflow-wrap: anywhere; color: var(--ink);
  background: var(--surface-sunk); border-left: var(--pin) solid var(--mark-line);
  padding: var(--s-2) var(--s-3); margin: var(--s-2) 0 0;
  user-select: all; -webkit-user-select: all;
}
/* user-select: all, because a recovery code half-selected is a colleague
   locked out of their own account. */
.codes {
  list-style: none; padding: 0; margin: var(--s-4) 0;
  display: grid; grid-template-columns: 1fr 1fr; gap: var(--s-2) var(--s-4);
  font-family: var(--data); font-size: var(--t-base); font-weight: var(--w-data);
  color: var(--ink); user-select: all; -webkit-user-select: all;
}
a { color: var(--mark); }

@media (prefers-contrast: more) {
  /* Theme-agnostic because both sides are primitives: whichever block is
     live supplies the stronger value. */
  :root { --p-rule: var(--p-rule-strong); --p-ink-3: var(--p-ink-2); }
}
@media (forced-colors: active) {
  :where(a, button, input, summary):focus-visible { outline-color: Highlight; }
  .rung__seg--on, .switch__opt[aria-pressed='true'] { background: Highlight; }
}

/* ====================================================================== *
 * NARROW
 *
 * Measured at 390px: the strip wraps its tools onto a second line, and
 * the six boxes give up their gap and six pixels of height before they
 * give up their type size — a 24px figure is the clearest thing on the
 * screen and it is the thing the reader is copying from a phone.
 * ====================================================================== */
@media (max-width: 560px) {
  .bar { padding: var(--s-3) var(--s-4); }
  .stage { padding: var(--s-4) var(--s-4) var(--s-6); }
  .sheet { padding: var(--s-4) var(--s-4) var(--s-3); }
  .otp { gap: 6px; }
  input.otp__box { min-height: 52px; }
  input.otp__box:nth-child(4) { margin-left: 6px; }
  .codes { grid-template-columns: 1fr; }
}
