/*!
 * XeniLabs - login screen.
 *
 * Standalone on purpose: the login page loads neither AdminLTE, nor Bootstrap,
 * nor jQuery, nor a third-party icon kit. An authentication page is the one
 * screen where every extra script is an extra way in, and dropping them is what
 * lets the page ship a strict Content-Security-Policy (see LoginGuard).
 *
 * The tokens below mirror dist/css/theme.css so the screen belongs to the same
 * interface as the dashboard behind it. Change a token, not a rule.
 */

/* ==========================================================================
   1. Tokens
   ========================================================================== */

:root {
  --brand:        #047857;
  --brand-600:    #036349;
  --brand-700:    #03543d;
  --brand-050:    #e6f2ee;
  --brand-lite:   #2fbe86;   /* readable green on a dark ground */

  /* Everything that marks "this is where you are typing": focus ring, active
     code box, caret. Its own token because the brand green is dark by design -
     right on white, nearly invisible on the dark surface - and a focus mark
     that cannot be seen is not a focus mark. */
  --focus:        var(--brand);

  --bg:           #eef2f5;
  --surface:      #ffffff;
  --surface-2:    #f8fafc;
  --border:       #e6eaef;
  --border-strong:#d3dae2;
  --ink:          #1f2933;
  --ink-2:        #52606d;
  --ink-3:        #7b8794;

  --danger:       #e0342b;
  --danger-050:   #fdecec;
  --danger-ink:   #b3241d;
  --warning:      #f0a020;
  --warning-050:  #fdf3e3;
  --warning-ink:  #a86a05;
  --info:         #0ea5e9;
  --info-050:     #e6f6fd;
  --info-ink:     #0b7ea8;
  --success-050:  var(--brand-050);
  --success-ink:  var(--brand-700);

  --r-sm:         8px;
  --r:            12px;
  --r-lg:         18px;
  --r-pill:       999px;

  --shadow-sm:    0 1px 2px rgba(16, 24, 40, .05);
  --shadow-lg:    0 24px 60px rgba(16, 24, 40, .12), 0 2px 6px rgba(16, 24, 40, .05);

  --font: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
  --ease: .18s ease;

  color-scheme: light;
}

/*
 * Dark mode follows the system by default, and `data-theme` on <html> overrides
 * it in both directions: the server prints the session preference there (the
 * same one the sidebar switch stores), so the page never flashes the wrong
 * palette before the stylesheet is applied.
 */
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    --bg:            #10161c;
    --surface:       #1a222b;
    --surface-2:     #212b35;
    --border:        #2c3742;
    --border-strong: #3a4753;
    --ink:           #e7edf3;
    --ink-2:         #b3bec9;
    --ink-3:         #8593a0;

    --brand-050:     #10312a;
    --focus:         var(--brand-lite);
    --danger-050:    #3a1a18;
    --danger-ink:    #ff9a93;
    --warning-050:   #362a13;
    --warning-ink:   #f3c46a;
    --info-050:      #102b38;
    --info-ink:      #7dd3f5;
    --success-050:   var(--brand-050);
    --success-ink:   var(--brand-lite);

    --shadow-lg:     0 24px 60px rgba(0, 0, 0, .55);

    color-scheme: dark;
  }
}

:root[data-theme="dark"] {
  --bg:            #10161c;
  --surface:       #1a222b;
  --surface-2:     #212b35;
  --border:        #2c3742;
  --border-strong: #3a4753;
  --ink:           #e7edf3;
  --ink-2:         #b3bec9;
  --ink-3:         #8593a0;

  --brand-050:     #10312a;
  --focus:         var(--brand-lite);
  --danger-050:    #3a1a18;
  --danger-ink:    #ff9a93;
  --warning-050:   #362a13;
  --warning-ink:   #f3c46a;
  --info-050:      #102b38;
  --info-ink:      #7dd3f5;
  --success-050:   var(--brand-050);
  --success-ink:   var(--brand-lite);

  --shadow-lg:     0 24px 60px rgba(0, 0, 0, .55);

  color-scheme: dark;
}

/* ==========================================================================
   2. Base
   ========================================================================== */

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

html,
body { height: 100%; }

body {
  margin: 0;
  font-family: var(--font);
  font-size: 15px;
  line-height: 1.55;
  color: var(--ink);
  background: var(--bg);
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
}

a {
  color: var(--brand);
  text-decoration: none;
  transition: color var(--ease);
}

:root[data-theme="dark"] a { color: var(--brand-lite); }

@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) a { color: var(--brand-lite); }
}

a:hover,
a:focus { text-decoration: underline; }

:focus-visible {
  outline: 2px solid var(--focus);
  outline-offset: 2px;
  border-radius: 4px;
}

/* ==========================================================================
   3. Layout - brand panel on the left, form on the right
   ========================================================================== */

.auth {
  display: grid;
  grid-template-columns: minmax(0, 1.05fr) minmax(0, 1fr);
  min-height: 100%;
}

/* --- Brand panel ---------------------------------------------------------- */

.auth-brand {
  position: relative;
  display: flex;
  flex-direction: column;
  justify-content: space-between;
  padding: 48px 56px;
  overflow: hidden;
  color: #fff;
  background: linear-gradient(150deg, #065f46 0%, var(--brand) 45%, #0b3d33 100%);
}

/* Faint grid, the way a supervision screen looks from a distance. */
.auth-brand::before {
  content: "";
  position: absolute;
  inset: -20%;
  background-image:
    linear-gradient(rgba(255, 255, 255, .06) 1px, transparent 1px),
    linear-gradient(90deg, rgba(255, 255, 255, .06) 1px, transparent 1px);
  background-size: 46px 46px;
  transform: rotate(-8deg);
  pointer-events: none;
}

/* Light spill from the top right corner, so the flat gradient gets a source. */
.auth-brand::after {
  content: "";
  position: absolute;
  top: -180px;
  right: -140px;
  width: 460px;
  height: 460px;
  border-radius: 50%;
  background: radial-gradient(circle, rgba(52, 211, 153, .38) 0%, rgba(52, 211, 153, 0) 70%);
  pointer-events: none;
}

.auth-brand > * {
  position: relative;
  z-index: 1;
}

.brand-mark {
  display: inline-flex;
  align-items: center;
  gap: 12px;
  font-size: 22px;
  letter-spacing: -.01em;
  color: #fff;
}

.brand-mark:hover,
.brand-mark:focus { text-decoration: none; }

.brand-mark .dot {
  display: grid;
  place-items: center;
  width: 38px;
  height: 38px;
  border: 1px solid rgba(255, 255, 255, .28);
  border-radius: 11px;
  background: rgba(255, 255, 255, .14);
}

.brand-mark .dot svg { width: 20px; height: 20px; }

.brand-env {
  margin-left: 4px;
  padding: 2px 9px;
  font-size: 11px;
  font-weight: 600;
  letter-spacing: .06em;
  text-transform: uppercase;
  border: 1px solid rgba(255, 255, 255, .3);
  border-radius: var(--r-pill);
  background: rgba(255, 255, 255, .12);
}

.brand-pitch h1 {
  margin: 0 0 14px;
  font-size: 34px;
  line-height: 1.2;
  font-weight: 600;
  letter-spacing: -.02em;
}

.brand-pitch p {
  max-width: 34ch;
  margin: 0 0 30px;
  font-size: 15px;
  color: rgba(255, 255, 255, .82);
}

.brand-points {
  margin: 0;
  padding: 0;
  list-style: none;
  display: grid;
  gap: 14px;
}

.brand-points li {
  display: flex;
  align-items: flex-start;
  gap: 12px;
  font-size: 14px;
  color: rgba(255, 255, 255, .9);
}

.brand-points svg {
  flex: 0 0 auto;
  width: 18px;
  height: 18px;
  margin-top: 2px;
  opacity: .85;
}

.brand-foot {
  font-size: 12.5px;
  color: rgba(255, 255, 255, .62);
}

.brand-foot a { color: rgba(255, 255, 255, .82); }

/* --- Form side ------------------------------------------------------------ */

.auth-main {
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 40px 24px;
}

.auth-card {
  width: 100%;
  max-width: 420px;
}

.auth-head {
  margin-bottom: 26px;
}

.auth-head h2 {
  margin: 0 0 6px;
  font-size: 25px;
  font-weight: 600;
  letter-spacing: -.02em;
}

.auth-head p {
  margin: 0;
  color: var(--ink-2);
  font-size: 14.5px;
}

/* ==========================================================================
   4. Form
   ========================================================================== */

.field {
  margin-bottom: 16px;
}

.field > label {
  display: block;
  margin-bottom: 6px;
  font-size: 13px;
  font-weight: 600;
  color: var(--ink-2);
}

.control {
  position: relative;
  display: flex;
  align-items: center;
}

.control > svg {
  position: absolute;
  left: 14px;
  width: 18px;
  height: 18px;
  color: var(--ink-3);
  pointer-events: none;
  transition: color var(--ease);
}

.control input {
  width: 100%;
  height: 48px;
  padding: 0 14px 0 44px;
  font: inherit;
  font-size: 15px;
  color: var(--ink);
  background: var(--surface);
  border: 1px solid var(--border-strong);
  border-radius: var(--r);
  transition: border-color var(--ease), box-shadow var(--ease), background var(--ease);
  -webkit-appearance: none;
  appearance: none;
}

.control input::placeholder { color: var(--ink-3); }

.control input:hover { border-color: var(--ink-3); }

.control input:focus {
  outline: none;
  border-color: var(--focus);
  box-shadow: 0 0 0 4px var(--brand-050);
}

.control input:focus ~ svg,
.control:focus-within > svg { color: var(--focus); }

/* Chrome paints its own yellow ground on an autofilled input, which survives a
   background declaration - only an inset shadow covers it. */
.control input:-webkit-autofill,
.control input:-webkit-autofill:hover,
.control input:-webkit-autofill:focus {
  -webkit-text-fill-color: var(--ink);
  box-shadow: 0 0 0 1000px var(--surface) inset;
  transition: background-color 5000s ease-in-out 0s;
}

.control.has-action input { padding-right: 46px; }

.reveal {
  position: absolute;
  right: 6px;
  display: grid;
  place-items: center;
  width: 36px;
  height: 36px;
  padding: 0;
  color: var(--ink-3);
  background: none;
  border: 0;
  border-radius: var(--r-sm);
  cursor: pointer;
  transition: color var(--ease), background var(--ease);
}

.reveal:hover {
  color: var(--ink);
  background: var(--surface-2);
}

.reveal svg { width: 18px; height: 18px; }

.reveal .icon-off { display: none; }
.reveal.is-on .icon-on { display: none; }
.reveal.is-on .icon-off { display: block; }

/* --- One-time code -------------------------------------------------------- */

/*
 * Two states for one field.
 *
 * Without the script: a single wide input, centred and tracked out - readable,
 * and the only thing a browser with no JavaScript can offer.
 *
 * With it (`.is-segmented`): six boxes, and the input goes transparent on top
 * of them. Keeping one real input rather than six is what preserves the code
 * suggestion from the phone, the paste, the autofill and the maxlength - the
 * boxes are decoration, painted from the value.
 */

/* No icon and no placeholder gutter here: the code is the whole content of the
   field, so it gets the full width and sits in the middle of it. */
.code-field input {
  width: 100%;
  height: 62px;
  padding: 0 12px;
  font: inherit;
  font-size: 30px;
  font-weight: 600;
  font-variant-numeric: tabular-nums;
  letter-spacing: .38em;
  /* the tracking is added after the last digit too, which pushes the string
     off-centre; half of it back on the left evens it out */
  text-indent: .38em;
  text-align: center;
  color: var(--ink);
  background: var(--surface);
  border: 1px solid var(--border-strong);
  border-radius: var(--r);
  transition: border-color var(--ease), box-shadow var(--ease);
  -webkit-appearance: none;
  appearance: none;
}

.code-field input::placeholder {
  color: var(--ink-3);
  opacity: .55;
  font-weight: 400;
}

.code-field input:hover { border-color: var(--ink-3); }

.code-field input:focus {
  outline: none;
  border-color: var(--focus);
  box-shadow: 0 0 0 4px var(--brand-050);
}

/* Segmented form ----------------------------------------------------------- */

.code-field.is-segmented {
  position: relative;
}

.code-field.is-segmented .code-cells {
  display: flex;
  gap: 10px;
}

/* The input covers the whole row and disappears: no caret to align with the
   boxes, no double rendering of the digits. 16px keeps iOS from zooming the
   page when it takes focus, even though nothing of it is visible. */
.code-field.is-segmented input {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  padding: 0;
  font-size: 16px;
  letter-spacing: normal;
  text-indent: 0;
  color: transparent;
  caret-color: transparent;
  background: none;
  border: 0;
  box-shadow: none;
  cursor: text;
}

.code-field.is-segmented input:hover,
.code-field.is-segmented input:focus {
  border: 0;
  box-shadow: none;
}

.code-cell {
  display: grid;
  place-items: center;
  flex: 1 1 0;
  min-width: 0;
  height: 62px;
  font-size: 26px;
  font-weight: 600;
  font-variant-numeric: tabular-nums;
  line-height: 1;
  color: var(--ink);
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--r);
  transition: border-color var(--ease), box-shadow var(--ease), background var(--ease);
}

/* A digit already entered: the box firms up, so the eye can count what is done
   without reading the digits. */
.code-cell.is-filled {
  border-color: var(--border-strong);
}

.code-cell.is-active {
  border-color: var(--focus);
  box-shadow: 0 0 0 4px var(--brand-050);
}

/* The caret we draw ourselves, in the box waiting for a digit. A filled active
   box gets no bar: there is nothing left to type in it. */
.code-cell.is-active:not(.is-filled)::after {
  content: "";
  width: 2px;
  height: 26px;
  background: var(--focus);
  border-radius: 1px;
  animation: caret 1.06s steps(2, start) infinite;
}

@keyframes caret {
  to { visibility: hidden; }
}

/* A refused code: the row leans red and moves once, so a second failure on the
   same screen is still noticed. */
.code-field.has-error .code-cell {
  border-color: rgba(224, 52, 43, .45);
}

.code-field.has-error .code-cells {
  animation: shake .4s ease;
}

@keyframes shake {
  25% { transform: translateX(-5px); }
  50% { transform: translateX(4px); }
  75% { transform: translateX(-2px); }
}

@media (prefers-reduced-motion: reduce) {
  .code-cell.is-active:not(.is-filled)::after { animation: none; }
  .code-field.has-error .code-cells { animation: none; }
}

@media (max-width: 400px) {
  .code-field.is-segmented .code-cells { gap: 7px; }
  .code-cell { height: 56px; font-size: 23px; }
}

.field-help {
  margin: 8px 0 0;
  font-size: 13px;
  color: var(--ink-3);
}

/* Expiry of the step, under the button. Quiet on purpose: it is information,
   not a warning - until it runs out, and then the page reloads itself. */
.auth-meta {
  margin: 16px 0 0;
  text-align: center;
  font-size: 13.5px;
  color: var(--ink-3);
}

.countdown-inline {
  font-weight: 600;
  font-variant-numeric: tabular-nums;
  color: var(--ink-2);
}

/* The honeypot. Not `display:none`: a bot that reads the stylesheet skips a
   hidden field, but rarely bothers with one that is merely pushed out of the
   viewport. Kept out of the tab order and out of the accessibility tree so no
   real user can ever reach it. */
.trap {
  position: absolute !important;
  left: -9999px;
  width: 1px;
  height: 1px;
  overflow: hidden;
  opacity: 0;
}

/* --- Remember me ---------------------------------------------------------- */

.switch-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  margin: 4px 0 22px;
}

.switch {
  display: inline-flex;
  align-items: center;
  gap: 10px;
  font-size: 14px;
  color: var(--ink-2);
  cursor: pointer;
  user-select: none;
}

.switch input {
  position: absolute;
  opacity: 0;
  width: 0;
  height: 0;
}

.switch .track {
  position: relative;
  flex: 0 0 auto;
  width: 40px;
  height: 22px;
  background: var(--border-strong);
  border-radius: var(--r-pill);
  transition: background var(--ease);
}

.switch .track::after {
  content: "";
  position: absolute;
  top: 3px;
  left: 3px;
  width: 16px;
  height: 16px;
  background: #fff;
  border-radius: 50%;
  box-shadow: var(--shadow-sm);
  transition: transform var(--ease);
}

.switch input:checked + .track { background: var(--brand); }
.switch input:checked + .track::after { transform: translateX(18px); }
.switch input:focus-visible + .track { box-shadow: 0 0 0 4px var(--brand-050); }

.switch-row .link-small { font-size: 14px; }

/* --- Button --------------------------------------------------------------- */

.btn-primary {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 10px;
  width: 100%;
  height: 48px;
  font: inherit;
  font-size: 15px;
  font-weight: 600;
  color: #fff;
  background: var(--brand);
  border: 0;
  border-radius: var(--r);
  cursor: pointer;
  transition: background var(--ease), transform var(--ease), box-shadow var(--ease);
}

.btn-primary:hover {
  background: var(--brand-600);
  box-shadow: 0 6px 18px rgba(4, 120, 87, .28);
}

.btn-primary:active { transform: translateY(1px); }

.btn-primary[disabled] {
  opacity: .65;
  cursor: not-allowed;
  box-shadow: none;
}

.btn-primary svg { width: 18px; height: 18px; }

.btn-primary .spinner {
  width: 17px;
  height: 17px;
  border: 2px solid rgba(255, 255, 255, .35);
  border-top-color: #fff;
  border-radius: 50%;
  animation: spin .7s linear infinite;
}

@keyframes spin { to { transform: rotate(360deg); } }

@media (prefers-reduced-motion: reduce) {
  .btn-primary .spinner { animation-duration: 2s; }
}

/* --- Separator and secondary links ---------------------------------------- */

.sep {
  display: flex;
  align-items: center;
  gap: 14px;
  margin: 24px 0;
  font-size: 12px;
  letter-spacing: .08em;
  text-transform: uppercase;
  color: var(--ink-3);
}

.sep::before,
.sep::after {
  content: "";
  flex: 1;
  height: 1px;
  background: var(--border);
}

.auth-links {
  display: flex;
  flex-direction: column;
  gap: 10px;
  text-align: center;
  font-size: 14px;
  color: var(--ink-2);
}

/* ==========================================================================
   5. Messages
   ========================================================================== */

.alert {
  display: flex;
  align-items: flex-start;
  gap: 11px;
  margin-bottom: 20px;
  padding: 13px 15px;
  font-size: 14px;
  border: 1px solid transparent;
  border-radius: var(--r);
}

.alert svg {
  flex: 0 0 auto;
  width: 18px;
  height: 18px;
  margin-top: 2px;
}

.alert p { margin: 0; }
.alert p + p { margin-top: 4px; }

.alert-danger  { color: var(--danger-ink);  background: var(--danger-050);  border-color: rgba(224, 52, 43, .22); }
.alert-warning { color: var(--warning-ink); background: var(--warning-050); border-color: rgba(240, 160, 32, .25); }
.alert-info    { color: var(--info-ink);    background: var(--info-050);    border-color: rgba(14, 165, 233, .22); }
.alert-success { color: var(--success-ink); background: var(--success-050); border-color: rgba(4, 120, 87, .22); }

/* ==========================================================================
   6. Locked state
   ========================================================================== */

.locked {
  text-align: center;
  padding: 8px 0 4px;
}

.locked .shield {
  display: grid;
  place-items: center;
  width: 66px;
  height: 66px;
  margin: 0 auto 20px;
  color: var(--danger-ink);
  background: var(--danger-050);
  border-radius: 50%;
}

.locked .shield svg { width: 30px; height: 30px; }

/* Same block, opposite news: the confirmation that a mail went out. The red
   of an alert has no business there. */
.locked.is-sent .shield {
  color: var(--success-ink);
  background: var(--success-050);
}

.locked h2 {
  margin: 0 0 8px;
  font-size: 22px;
  font-weight: 600;
}

.locked p {
  margin: 0 auto 18px;
  max-width: 38ch;
  color: var(--ink-2);
  font-size: 14.5px;
}

.countdown {
  display: inline-block;
  min-width: 128px;
  padding: 11px 20px;
  font-size: 24px;
  font-weight: 600;
  font-variant-numeric: tabular-nums;
  letter-spacing: .02em;
  color: var(--ink);
  background: var(--surface-2);
  border: 1px solid var(--border);
  border-radius: var(--r);
}

/* ==========================================================================
   7. Footer bits
   ========================================================================== */

.auth-legal {
  margin-top: 26px;
  font-size: 12.5px;
  line-height: 1.6;
  color: var(--ink-3);
  text-align: center;
}

.auth-legal a { color: var(--ink-3); text-decoration: underline; }

/* The reCAPTCHA badge overlaps the bottom right corner of the page; the notice
   below replaces it, as Google's terms allow. */
.grecaptcha-badge { visibility: hidden; }

/* ==========================================================================
   8. Responsive
   ========================================================================== */

@media (max-width: 1024px) {
  .auth { grid-template-columns: 1fr; }

  .auth-brand {
    padding: 28px 28px 34px;
    gap: 22px;
  }

  .brand-pitch h1 { font-size: 25px; }
  .brand-pitch p { margin-bottom: 0; }
  .brand-points,
  .brand-foot { display: none; }

  .auth-main { padding: 34px 20px 48px; }
}

@media (max-width: 560px) {
  .auth-brand { padding: 22px 20px 26px; }
  .brand-pitch h1 { font-size: 22px; }
  .brand-pitch p { font-size: 14px; }
  .auth-head h2 { font-size: 22px; }
}

/* On a short landscape phone the brand panel is pure loss of space. */
@media (max-height: 520px) and (max-width: 1024px) {
  .auth-brand { padding: 16px 20px; }
  .brand-pitch { display: none; }
}
