/*
  The door: the sign-in page and the forgotten-password page.

  Not app.css. That is for people who are already signed in, and loading it here
  would ship the whole console to anyone who can reach the door — which is why
  these pages were standalone in the first place, with their tokens written out
  by hand rather than imported.

  Shared between the two doors, though, because there are now two. The tokens
  below were copied once already and drifted while nobody was looking: this
  page's accent had wandered to its own brighter blue, so the first colour the
  operator ever saw was one the product does not use again. A second hand-copied
  page would drift the same way, twice as fast.
*/

  :root {
    color-scheme: light;
    /* Deeper than the console's bar: this page has no content competing with
       it, so it can go darker and let the card carry all the brightness.

       Cool rather than neutral, and pulled from the accent's own hue: against a
       flat grey the steel blue below read as a smudge on black, because nothing
       else on the page agreed with it. */
    --void: #171c22;
    /* The console's --accent (#4a6377), which is the blue the operator sees for
       the next eight hours. This page had drifted to its own brighter #4a6b8a,
       so the first thing they saw was a colour the product does not use again.

       Components rather than a hex: its only use is inside rgba(), which cannot
       take one. Keep it in step with --accent in app.css. */
    --accent-rgb: 74, 99, 119;
    --panel: #ffffff;
    --line: #e0e3e7;
    --text: #1f2429;
    --muted: #6b7280;
    --faint: #6e767e;
    --bad: #b3403a;
    --bad-bg: #f9ecec;
    --sans: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
    /* The key field asks for this by name and it was never declared here, so
       the declaration was invalid and the key was typed in the sans face — on
       the one input whose whole point is being checked character by character
       against a key handed over in monospace. */
    --mono: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, monospace;

    /* The console's anthracite. The copied rules below reference --ink by name,
       so it has to exist here under that name — renaming it to --void for the
       page background is exactly what made the button invisible once. */
    --ink: #2b3036;

    /* Copied verbatim from the console's stylesheet. A sign-in form that uses
       taller inputs and a different label voice than the app behind it makes
       the two feel like different products at the moment of first contact. */
    --ctl-md: 32px;
    --ctl-px-sm: 9px;
    --ctl-px-md: 12px;
    --fs-sm: 12px;
    --fs-base: 13px;
    --r-ctl: 6px;
    --sp-1: 4px;
    --sp-3: 12px;
  }

  * { box-sizing: border-box; }

  /*
    `hidden` has to win.

    The button rule below sets `display: inline-flex`, and an author display
    beats the user agent's `[hidden] { display: none }` — so hiding a button by
    setting the attribute did nothing at all, and the recovery link stayed on
    screen next to the form it was supposed to reveal. One rule, before anything
    that sets display, so nothing on this page can hide by accident again.
  */
  [hidden] { display: none !important; }

  /* 14px/1.55, the console's body type. */
  body {
    margin: 0; min-height: 100vh;
    display: grid; grid-template-rows: 1fr auto 1.15fr; justify-items: center;
    background: var(--void); color: var(--text);
    font: 14px/1.55 var(--sans); -webkit-font-smoothing: antialiased;
    overflow-x: hidden;
  }

  /*
    ── The ground ────────────────────────────────────────────────────────────

    The mark, small, in the bottom-right corner, cut by both edges.

    Earlier versions filled the frame and slid behind the card. That put the
    logo in the same place as the thing you came here to do, and a sign-in
    screen has exactly one job. This gets out of the way: it sits where nothing
    else is, at a size that is a signature rather than a backdrop.

    Cropped by the corner on purpose — a mark that ends inside the frame is
    placed, a mark that runs past it was already there.
  */
  .watermark {
    position: fixed; z-index: 0; pointer-events: none;
    right: -5vh; bottom: -6vh;
    height: 40vh; width: auto;
    fill: #ffffff; opacity: .075;
  }

  /* The card is nearly full width on a phone, so the corner has less room. */
  @media (max-width: 720px) {
    .watermark { right: -9vw; bottom: -4vh; height: 26vh; }
  }

  /*
    Depth under it: a pool of light where the card goes, steel blue rising from
    below, and corners that fall away.

    The blue was centred at 118% — eighteen percent below the bottom edge, with
    the fade running out at 66% of a 62% radius. Every part of it bright enough
    to be a colour was off-screen, so the brand colour on the one screen that
    introduces the product was, in practice, not there. It sits at the edge now,
    where the strong half is on the page.

    The white pool comes down as it rises: two lights of equal strength is how a
    ground turns to grey haze, and the blue is the one that means something.
  */
  body::before {
    content: ""; position: fixed; inset: 0; z-index: 0; pointer-events: none;
    background:
      radial-gradient(42% 32% at 50% 46%, rgba(255,255,255,.05), rgba(255,255,255,0) 72%),
      radial-gradient(105% 58% at 50% 100%, rgba(var(--accent-rgb),.50), rgba(var(--accent-rgb),0) 70%),
      radial-gradient(80% 72% at 50% 44%, rgba(0,0,0,0), rgba(0,0,0,.40) 100%);
  }

  main { grid-row: 2; position: relative; z-index: 1; width: 100%; max-width: 372px; padding: 0 24px; }

  .brand { display: grid; justify-items: center; margin-bottom: 26px; }
  /* Icon and word are one drawn object on one line, at a size that introduces
     the product rather than dominating the page. */
  .logo { display: block; width: 124px; height: auto; color: #fff; }
  .slogan {
    margin: 12px 0 0; color: rgba(255,255,255,.34);
    font-size: 10.5px; font-weight: 600; letter-spacing: .15em; text-transform: uppercase;
  }

  /* ── The card ─────────────────────────────────────────────────────────── */

  /*
    The raised white card, and only where it is asked for.
  
    This was on the bare `form` element, which was fine while the page held one
    form. It now holds two — the sign-in fields and the recovery key — and the
    second sits INSIDE the waiting card, so it drew a card within a card,
    shadow and all. A class is applied to the two things that are cards and to
    nothing that merely happens to be a form.
  */
  .card {
    position: relative;
    background: var(--panel); border-radius: 12px; padding: 26px 24px 24px;
    /* Three shadows doing three jobs: a hairline to define the edge, a close
       shadow for the lift, and a wide soft one for the depth. One shadow can
       only ever do one of them. */
    box-shadow:
      0 0 0 1px rgba(255,255,255,.06),
      0 2px 6px rgba(0,0,0,.22),
      0 24px 56px -18px rgba(0,0,0,.62);
  }

  /* A light source. Without it a white rectangle on a dark field reads as flat
     paper rather than as a raised surface. */
  .card::before {
    content: ""; position: absolute; inset: 0 0 auto; height: 1px;
    border-radius: 12px 12px 0 0;
    background: linear-gradient(90deg, transparent, rgba(255,255,255,.9) 22%, rgba(255,255,255,.9) 78%, transparent);
  }

  /* label, input and button below are the console's rules verbatim. */
  label { display: block; font-size: var(--fs-sm); color: var(--muted); margin-bottom: var(--sp-1); }
  .field { margin-bottom: var(--sp-3); }

  input {
    width: 100%; height: var(--ctl-md); padding: 0 var(--ctl-px-sm);
    font: 400 var(--fs-base) var(--sans);
    border: 1px solid var(--line); border-radius: var(--r-ctl);
    background: var(--panel); color: var(--text);
    transition: border-color .12s ease;
  }
  input::placeholder { color: var(--faint); }
  input:hover:not(:disabled) { border-color: #cbcec9; }
  input:focus-visible { outline: 2px solid var(--ink); outline-offset: 1px; }

  button {
    display: inline-flex; align-items: center; justify-content: center;
    width: 100%; height: var(--ctl-md); margin-top: var(--sp-3);
    padding: 0 var(--ctl-px-md);
    font: 500 var(--fs-base)/1 var(--sans); border-radius: var(--r-ctl);
    border: 1px solid var(--ink); background: var(--ink); color: #fff;
    cursor: pointer; white-space: nowrap;
    transition: background .12s ease, border-color .12s ease;
  }
  button:hover:not(:disabled) { background: #262b34; border-color: #262b34; }
  button:disabled { opacity: .4; cursor: default; }
  button:focus-visible { outline: 2px solid var(--ink); outline-offset: 2px; }

  /*
    On a phone the fields take 16px type, and the room to hold it.

    iOS Safari zooms the page when a field whose type is under 16px takes focus.
    The viewport deliberately allows zoom — pinch is an accessibility
    affordance, not something to switch off with maximum-scale — so the zoom
    happens: tap Username and the form leaps to fill the screen with the
    password field somewhere off the edge, and you pinch back out to carry on.
    The console's 13px is right at a desk and wrong in a hand, and this is the
    one form every operator has to get through before they can reach anything.

    44px because 16px needs the room, and because 32px is under every
    touch-target guideline there is — on the page whose only two targets are
    these. It is the one place the sign-in page departs from the console's
    control metrics, and it departs to stay usable rather than to look
    different.

    Keyed on the pointer as well as the width, because width alone gets this
    wrong the moment the phone is turned: a handset in landscape is 812px and
    up, outside any sensible mobile breakpoint, so the fields went back to 13px
    and the zoom came back with them. The width arm stays for a narrow window on
    a desktop, where the pointer is fine but the room is not.
  */
  @media (max-width: 720px), (pointer: coarse) {
    input, button { height: 44px; font-size: 16px; }
    label { font-size: 13px; }
  }

  /* ── Waiting on Telegram ──────────────────────────────────────────────── */

  /* The waiting card is the same card, centred. Nothing else differs, so
     nothing else is repeated — the duplicate that was here is how the two
     drifted a pixel apart in padding in the first place. */
  #wait { text-align: center; padding-bottom: 22px; }
  .wait-state { margin: 0; font-size: var(--fs-base); color: var(--muted); }
  /* When nothing was delivered, the state is a failure, not a prompt — so it
     reads in the error colour rather than the muted one. */
  .wait-state.is-error { color: var(--bad); font-weight: 600; }
  /* The number is the control here — it is what the operator has to carry
     across to their phone, so nothing else on the card competes with it. */
  .wait-code {
    margin: 10px 0 12px; font: 600 46px/1 var(--sans);
    letter-spacing: .06em; color: var(--text); font-variant-numeric: tabular-nums;
  }
  .wait-note {
    margin: 0 auto; max-width: 30ch;
    font-size: var(--fs-sm); line-height: 1.5; color: var(--faint);
  }
  .wait-left {
    margin: 14px 0 0; font: 500 var(--fs-sm) var(--sans);
    color: var(--faint); font-variant-numeric: tabular-nums; min-height: 1em;
  }
  /* A link, not a button: it is the alternative to what the card is asking for,
     and giving it a button's weight would make the two look like a choice
     rather than a fallback. */
  .wait-alt {
    display: block; width: auto; height: auto; margin: 12px auto 0; padding: 0;
    background: none; border: 0; color: var(--muted); text-align: center;
    font: 400 var(--fs-sm) var(--sans); text-decoration: underline;
    text-underline-offset: 2px; cursor: pointer;
  }
  .wait-alt:hover { color: var(--text); }
  /* The card that asks for a recovery key — the whole card when it is showing,
     rather than a block under the Telegram one, so it opens on its own mark
     instead of under another heading.

     A class rather than #rec because two pages wear it now: the fallback
     halfway through a sign-in, and the first step of /login/forgot. */
  .keycard { text-align: left; }
  .keycard .field { margin-bottom: 10px; }
  /* A link, not a second button: the submit above it is the action, and giving
     both the same weight would make leaving look like a way of signing in. */
  #rec-back { margin-top: 14px; }
  /*
    The mark on top, in a tinted disc — the same shape the console uses on the
    sheet that handed the key over. Asking for it and giving it out are the two
    halves of one thing, and they should look like it: whoever saved this key
    last week sees the object they were given, before they read a word.
  */
  .rec-mark {
    display: grid; place-items: center; width: 48px; height: 48px; font-size: 22px;
    margin: 0 auto 10px; border-radius: 999px;
    background: rgba(var(--accent-rgb), .10); color: rgb(var(--accent-rgb));
  }
  .rec-head {
    margin: 0 0 4px; text-align: center; font: 600 15px/1.2 var(--sans); color: var(--text);
  }
  /* Balanced and capped, so the line does not break leaving one orphaned word
     under a centred heading. */
  .rec-sub {
    margin: 0 auto 14px; max-width: 30ch; text-align: center; text-wrap: balance;
    font-size: var(--fs-sm); line-height: 1.5; color: var(--faint);
  }
  /* A label the heading has already said. Hidden from the eye but not from a
     screen reader, which announces an input by its label and would otherwise
     reach an unnamed box.

     Opt-in rather than "every label in a key card": /login/forgot asks for a
     username and a key on one card, and a heading cannot name them both. */
  label.said-above {
    position: absolute; width: 1px; height: 1px; margin: -1px;
    padding: 0; overflow: hidden; clip-path: inset(50%); white-space: nowrap;
  }
  /* The same hint the console puts under a field, so a rule stated here reads
     the way a rule stated inside reads. */
  .hint {
    margin: var(--sp-1) 0 0; font-size: 11px; line-height: 1.5; color: var(--faint);
  }

  /* The field a key is typed into. Monospace because that is what it was handed
     over in, and centred because it is the whole subject of its card. */
  .key-input {
    font-family: var(--mono); letter-spacing: .06em; text-align: center;
    font-size: 15px;
  }
  .key-input::placeholder { letter-spacing: .06em; }

  /* A link that finishes a card, wearing the button it replaces. There is
     nothing left to submit at that point — the work is done and the only thing
     left is to go somewhere — but it is still the one thing to press. */
  .button-link {
    display: flex; align-items: center; justify-content: center;
    width: 100%; height: 38px; margin-top: 4px;
    background: var(--ink); color: #fff; border-radius: var(--r-ctl);
    font: 600 var(--fs-base) var(--sans); text-decoration: none;
  }
  .button-link:hover { background: #383f47; }

  /*
    The console's error line — see .banner.hard in app.css.

    Not a box. A bordered panel with a coloured rule down its left is the
    shape of a system notice from a generation ago, and it made "wrong
    password" look like a form letter. This is the mark and the words, in the
    status colour, and nothing drawn around them: it says what is wrong and
    gets out of the way of trying again. The mark means red is not the only
    signal, on the screen where being unable to read the signal costs most.
  */
  .problem {
    display: flex; align-items: flex-start; justify-content: center; gap: 7px;
    margin: 0 0 var(--sp-3); padding: 0;
    color: var(--bad); font-size: var(--fs-base); font-weight: 500; line-height: 1.45;
    text-align: left; animation: problem-in .18s ease-out;
  }
  .problem:empty { display: none; }
  .banner-icon { flex: none; font-size: 16px; margin-top: 1.5px; color: var(--bad); }
  /* Movement only, never opacity — see banner-in in app.css for why. */
  @keyframes problem-in {
    from { transform: translateY(-3px); }
    to   { transform: none; }
  }

  /* The line under a field that was refused — see fieldError(). Small, red,
     and unadorned: the ring on the field above it already says where. */
  .field-error {
    margin: 5px 0 0; font-size: var(--fs-sm); font-weight: 500;
    color: var(--bad); line-height: 1.4; animation: problem-in .18s ease-out;
  }
  /* While the error is up, the hint under the same field steps aside: "At
     least 12 characters." in red directly above "At least 12 characters." in
     grey is one sentence said twice, and the red one is the one that matters. */
  .field.is-bad .hint { display: none; }

  /* The field the message is about, marked until they start fixing it. The
     ring is the modern cue — the border says which field, the halo says it
     loudly enough to find without reading. */
  .field.is-bad > label { color: var(--bad); }
  .field.is-bad input { border-color: var(--bad); box-shadow: 0 0 0 3px rgba(179, 64, 58, .14); }
  .field.is-bad input:focus { outline-color: var(--bad); }

  /* The card shakes its head, once, the way a lock does. Short and small: a
     refusal, not an alarm. Restarted from script on each one — see say(). */
  .card.refused { animation: refused .36s cubic-bezier(.36, .07, .19, .97); }
  @keyframes refused {
    10%, 90% { transform: translateX(-1px); }
    20%, 80% { transform: translateX(2px); }
    30%, 50%, 70% { transform: translateX(-3px); }
    40%, 60% { transform: translateX(3px); }
  }

  @media (prefers-reduced-motion: reduce) { * { transition: none !important; animation: none !important; } }
