/* Ossintheus — the entry.

   One card over the ambient signal field. Its whole job is to say plainly what
   kind of session is about to start and then start it, so the copy is doing
   more work here than the layout is.

   Colours, radii, motion, and type all come from tokens.css. This file declares
   none of its own. */

body {
  margin: 0;
  min-height: 100dvh;
  background: var(--base);
  color: var(--text);
  font-family: var(--font);
  font-size: 15px;
  line-height: 1.55;
  -webkit-font-smoothing: antialiased;
}

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

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

.login-skip {
  position: fixed;
  top: 12px;
  left: 12px;
  z-index: 20;
  padding: 12px 18px;
  border-radius: var(--radius-sm);
  background: var(--text);
  color: var(--base);
  font-weight: 650;
  text-decoration: none;
  transform: translateY(-180%);
  transition: transform 200ms cubic-bezier(0.32, 0.72, 0, 1);
}

.login-skip:focus-visible { transform: translateY(0); }

.login-shell {
  position: relative;
  display: grid;
  place-items: center;
  min-height: 100dvh;
  padding: 32px 20px calc(32px + env(safe-area-inset-bottom));
  overflow: hidden;
}

/* The field is scenery. It sits behind everything, never intercepts a pointer,
   and stops painting entirely under reduced motion. */
.signal-field {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  pointer-events: none;
  z-index: 0;
}

/* One faint wash behind the card, and it does not move.

   This was two drifting aurora lobes. The page already has an ambient system —
   the signal field on the canvas — and running a second one behind it meant two
   things moving on their own timings for no reason either of them could explain.
   The product's own rule is that motion explains causality; ambient drift explains
   nothing, and on an entry screen it competes with the one sentence the operator is
   supposed to read. What is left is a static gradient that lifts the card off the
   ground and costs one paint. */
.login-shell::before {
  content: "";
  position: absolute;
  z-index: 0;
  inset: -25%;
  pointer-events: none;
  background:
    radial-gradient(38% 30% at 34% 32%, color-mix(in oklab, var(--accent) 13%, transparent), transparent 70%),
    radial-gradient(32% 26% at 68% 64%, color-mix(in oklab, var(--violet) 9%, transparent), transparent 72%);
  filter: blur(52px);
  opacity: 0.7;
}

/* A soft vignette so the card's edge reads without the card needing a heavier
   border. Opened up from 36% to 46% once the globe moved in behind it: the
   gradient was reaching full ground colour across exactly the part of the frame
   the world now occupies, and quietly erasing it. */
.login-shell::after {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 1;
  pointer-events: none;
  /* Loosened from `transparent 24% -> base 92%`, which reached full base colour
     well inside the viewport and flattened the aurora and the outer lattice
     into the background. The card still reads against a darker surround; the
     corners simply stop being pure black. */
  background: radial-gradient(135% 105% at 50% 44%, transparent 46%, var(--base) 100%);
}

.login-entry {
  position: relative;
  z-index: 2;
  width: min(30rem, 100%);
}

/* The card. A surface step and a hairline, which is the whole depth model the rest
   of the product uses — the entry has no reason to invent a second one.

   It used to be frosted glass over a 70px drop shadow, with a specular border that
   tracked the pointer. Three separate depth effects on the one screen that has a
   single job, in a product whose tokens file states outright that there is not one
   drop shadow in it. The blur also sat between the backdrop and the copy, which is
   what made the lede read soft at small sizes. */
.login-entry__inner {
  position: relative;
  display: flex;
  flex-direction: column;
  gap: 22px;
  padding: 32px;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background: var(--surface);
}

/* The accent, spent once, on the card's top edge — the same place every module
   panel spends it. It is the only colour on the entry, and it is what makes the
   screen belong to this product rather than to any dark login template. */
.login-entry__inner::after {
  content: "";
  position: absolute;
  inset: 0 0 auto;
  height: 2px;
  border-radius: var(--radius) var(--radius) 0 0;
  background: linear-gradient(90deg,
    var(--accent),
    color-mix(in oklab, var(--accent) 35%, transparent) 62%,
    transparent);
  pointer-events: none;
}

/* Entrance. The card settles, and the copy follows it in three beats rather than
   six: at 365ms the last block was still arriving after the operator had already
   read the first, which turns an entrance into a wait. The blocks that matter —
   the heading and the button — are on screen inside a third of a second.

   Transform and opacity only, so the whole sequence is composited. */
.login-entry__inner > * {
  animation: entry-rise 420ms var(--spring, cubic-bezier(0.32, 0.72, 0, 1)) both;
  animation-delay: var(--beat, 0ms);
}

.login-entry__inner > :nth-child(1) { --beat: 30ms; }
.login-entry__inner > :nth-child(2) { --beat: 80ms; }
.login-entry__inner > :nth-child(n + 3) { --beat: 130ms; }

@keyframes entry-rise {
  from { opacity: 0; transform: translate3d(0, 10px, 0); }
  to { opacity: 1; transform: translate3d(0, 0, 0); }
}

/* The card itself settles first, and slightly further, so the contents look
   like they are arriving inside something rather than floating in parallel. */
.login-entry {
  animation: card-settle 520ms var(--spring, cubic-bezier(0.32, 0.72, 0, 1)) both;
}

@keyframes card-settle {
  from { opacity: 0; transform: translate3d(0, 18px, 0) scale(0.99); }
  to { opacity: 1; transform: translate3d(0, 0, 0) scale(1); }
}

.login-brand {
  display: flex;
  align-items: center;
  gap: 11px;
}

.login-mark {
  width: 28px;
  height: 28px;
  color: var(--text);
}

/* templates/_mark.html: transparent, so it takes the theme through these */
.mark { display: block; width: 100%; height: 100%; object-fit: contain; }

.login-wordmark {
  font-size: 1rem;
  font-weight: 650;
  letter-spacing: -0.01em;
}

.entry-heading {
  display: flex;
  flex-direction: column;
  gap: 8px;
}

/* Martian, like every other eyebrow in the product. This was the only one set in
   the reading face, on the one screen a new operator sees first. */
.login-eyebrow {
  margin: 0;
  font-family: var(--label);
  font-weight: 500;
  font-size: var(--size-11);
  font-weight: 600;
  letter-spacing: 0.09em;
  text-transform: uppercase;
  color: var(--text-3);
}

.entry-heading h1 {
  margin: 0;
  font-size: 1.75rem;
  font-weight: 650;
  line-height: 1.15;
  letter-spacing: -0.02em;
}

.login-lede {
  margin: 0;
  max-width: 40ch;
  font-size: 0.9375rem;
  color: var(--text-2);
}

.login-actions { display: flex; }

.login-primary {
  position: relative;
  overflow: hidden;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  width: 100%;
  min-height: 54px;
  padding: 14px 20px;
  border: 1px solid transparent;
  border-radius: var(--radius-sm);
  background: var(--text);
  color: var(--base);
  font-size: 0.9375rem;
  font-weight: 650;
  text-decoration: none;
  cursor: pointer;
  transition: background-color 120ms cubic-bezier(0.4, 0, 0.2, 1),
              transform 160ms var(--spring, cubic-bezier(0.32, 0.72, 0, 1));
}

/* A sheen that crosses once on hover. It is masked inside the button and moves
   on transform, so it never repaints the card behind it. */
.login-primary::after {
  content: "";
  position: absolute;
  inset: 0;
  background: linear-gradient(
    100deg,
    transparent 38%,
    color-mix(in oklab, var(--accent) 34%, transparent) 50%,
    transparent 62%
  );
  transform: translate3d(-120%, 0, 0);
  pointer-events: none;
}

/* Hover tints the fill and sends the sheen across once. The lift and the coloured
   glow underneath it are gone: a shadow is the one depth cue this product does not
   own, and a button that rises off the card contradicts a card that does not rise
   off the page. The press below is what confirms the click. */
.login-primary:hover:not(:disabled) {
  background: color-mix(in oklab, var(--text) 88%, var(--accent));
}

.login-primary:hover:not(:disabled)::after {
  animation: primary-sheen 720ms cubic-bezier(0.22, 0.61, 0.36, 1);
}

@keyframes primary-sheen {
  to { transform: translate3d(120%, 0, 0); }
}

.login-primary:active:not(:disabled) {
  transform: translate3d(0, 0, 0) scale(0.982);
  transition-duration: 90ms;
}

/* The arrow leads the press. */
.login-primary svg {
  transition: transform 260ms var(--spring, cubic-bezier(0.32, 0.72, 0, 1));
}

.login-primary:hover:not(:disabled) svg { transform: translate3d(3px, 0, 0); }

.login-primary:disabled {
  background: var(--surface-2);
  border-color: var(--line);
  color: var(--text-3);
  cursor: not-allowed;
}

.login-primary svg { flex: none; width: 20px; height: 20px; }

/* "Sign in with your organisation": secondary to the button above it, one field and its own
   submit, so a work address goes to the organisation's provider (modules/agency_sso.py). */
.login-organisation {
  display: grid;
  gap: 8px;
  margin: 0;
}

.login-organisation label { display: grid; gap: 2px; }
.login-organisation label span { font-size: 0.875rem; font-weight: 600; color: var(--text); }
.login-organisation label small { font-size: 0.8125rem; line-height: 1.5; color: var(--text-2); }

.login-organisation__row { display: flex; gap: 8px; min-width: 0; }

.login-organisation__input {
  flex: 1 1 auto;
  min-width: 0;
  min-height: 44px;
  padding: 10px 12px;
  border: 1px solid var(--line-2);
  border-radius: var(--radius-sm);
  background: var(--surface-2);
  color: var(--text);
  font: inherit;
  font-size: 0.9375rem;
}

.login-organisation__input:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }

.login-organisation__go {
  flex: none;
  min-height: 44px;
  padding: 10px 16px;
  border: 1px solid var(--line-2);
  border-radius: var(--radius-sm);
  background: transparent;
  color: var(--text);
  font: inherit;
  font-size: 0.875rem;
  font-weight: 600;
  cursor: pointer;
}

.login-organisation__go:hover { border-color: var(--accent); color: var(--accent); }

.login-registration {
  margin: -8px 0 0;
  font-size: 0.8125rem;
  line-height: 1.6;
  color: var(--text-2);
}

.login-registration strong { color: var(--text); }

/* The privacy notice, at reading size and in the accent, with a tap target of 44px. */
.login-privacy-line {
  margin: 0;
  font-size: 0.875rem;
  line-height: 1.6;
  color: var(--text-2);
}

.login-privacy-line a {
  /* Inline, so the sentence does not break around it; the padding widens the
     target to about 44px without moving the lines. */
  padding-block: 13px;
  color: var(--accent);
  text-decoration: underline;
  text-underline-offset: 3px;
}

.login-privacy-line a:hover { color: var(--text); }

/* An identity provider that cannot be reached is a refusal, not a quiet
   disabled button, so it is stated in the same amber the product uses for every
   other uncertain state. */
.login-registration.is-warning {
  padding: 12px 14px;
  border: 1px solid color-mix(in oklab, var(--warn) 40%, transparent);
  border-radius: var(--radius-sm);
  background: color-mix(in oklab, var(--warn) 8%, transparent);
}

.login-registration.is-warning strong { color: var(--warn); }

/* A sign-in that came back unfinished (templates/sign_in_problem.html): the code is
   what the person can read out to an administrator, so it is legible but quiet. */
.login-problem-code {
  margin: -8px 0 0;
  font-size: 0.75rem;
  line-height: 1.6;
  color: var(--text-2);
}

.login-problem-code code {
  font-family: var(--mono);
  color: var(--text);
}

.login-assurance {
  display: flex;
  flex-direction: column;
  gap: 10px;
  padding-top: 18px;
  border-top: 1px solid var(--line);
}

.login-assurance > div {
  display: flex;
  align-items: flex-start;
  gap: 12px;
}

.login-assurance svg {
  flex: none;
  width: 20px;
  height: 20px;
  margin-top: 2px;
  color: var(--text-3);
}

.login-assurance span { display: flex; flex-direction: column; }
.login-assurance strong { font-size: 0.8125rem; font-weight: 600; }
.login-assurance small { font-size: 0.75rem; line-height: 1.55; color: var(--text-2); }

.login-entry__footer {
  display: flex;
  flex-wrap: wrap;
  gap: 8px 16px;
  justify-content: space-between;
  padding-top: 16px;
  border-top: 1px solid var(--line);
  font-family: var(--mono);
  font-size: 0.625rem;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--text-3);
}

@media (max-width: 760px) {
  .login-shell { padding: 20px 16px calc(24px + env(safe-area-inset-bottom)); }
  .login-entry__inner { padding: 24px 20px; gap: 18px; }
  .entry-heading h1 { font-size: 1.5rem; }
  .login-lede { font-size: 0.875rem; }
  .login-entry__footer { justify-content: flex-start; }
}

@media (prefers-reduced-motion: reduce) {
  /* The globe stays. A dot globe standing still is still a globe, and the script
     already pays to draw one static frame precisely so a reduced-motion visitor
     gets the world — hiding the canvas here threw that away and left 816px of the
     sign-in screen flat black with the card hanging off the right edge. Reduced
     motion removes motion, not content. */
  .login-skip,
  .login-primary,
  .login-primary svg { transition: none; }
  .login-primary:active:not(:disabled) { transform: none; }
  .login-primary:hover:not(:disabled) { transform: none; }
  /* Removed, not slowed. Every one of these is decoration; the card and its
     copy are what the page is for, and they must be on screen immediately. */
  .login-entry,
  .login-entry__inner > *,
  .login-primary::after { animation: none; }
  .login-entry__inner > * { opacity: 1; transform: none; }
}

/* In a forced-colours mode the tokens are replaced by the user's palette, so
   the card has to keep its structure from borders rather than from tint. */
@media (forced-colors: active) {
  .signal-field { display: none; }
  .login-shell::after { display: none; }
  .login-shell::before { display: none; }
  .login-entry__inner { border: 1px solid CanvasText; background: Canvas; }
  .login-entry__inner::after { display: none; }
  .login-primary { border: 1px solid ButtonText; }
  .login-registration.is-warning { border: 1px solid CanvasText; background: Canvas; }
}

/* ---------- the entry, beside the world ----------

   The card used to sit dead centre with an abstract lattice behind it. Now that
   the background is the actual globe, centring the card puts it on top of the
   only thing worth looking at. So on a wide screen the world takes the left and
   the card takes the right — the same composition the marketing site opens with,
   which is the point of sharing the drawing at all.

   Below 900px there is no room for two things side by side: the card comes back
   to the middle and the globe becomes what it always was there, a ground. */
@media (min-width: 900px) {
  .login-shell {
    place-items: center end;
    padding-inline: clamp(24px, 6vw, 96px);
  }

  .login-entry { margin-right: min(4vw, 64px); }
}

/* The display face. The login page loads tokens, fonts and this file — not the
   workspace stylesheet — so the rule that gives the shell its titles has to be
   repeated here rather than inherited. */
.login-wordmark,
.entry-heading h1 {
  font-family: var(--display);
  letter-spacing: -0.005em;
}

.entry-heading h1 { font-weight: 600; }

/* The privacy notice is a public page (/privacy), linked from the footer so it can be
   read before deciding to sign in. It takes the footer's type and is the one link in it. */
.login-entry__footer a {
  color: var(--text-2);
  text-decoration: underline;
  text-decoration-color: var(--line-2);
  text-underline-offset: 3px;
}

.login-entry__footer a:hover { color: var(--text); }

/* Language (static/i18n.js): a quiet control in the footer's own type. */
.login-language {
  padding: 0;
  border: 0;
  background: none;
  color: inherit;
  font: inherit;
  letter-spacing: inherit;
  text-transform: inherit;
  text-decoration: underline;
  text-underline-offset: 3px;
  cursor: pointer;
}

/* Public registration beside the identity-provider sign-in (templates/login.html): a quiet
   full-width secondary under the primary, the same radius, a little shorter. */
.login-actions { flex-direction: column; gap: 12px; }
.login-secondary {
  display: flex; align-items: center; justify-content: center; width: 100%; min-height: 48px;
  padding: 12px 20px; border: 1px solid var(--line); border-radius: var(--radius-sm);
  color: var(--text); background: transparent; font-size: .9375rem; font-weight: 650; text-decoration: none;
}
.login-secondary:hover { border-color: var(--text-3); background: var(--surface-2); }
.login-secondary:focus-visible { outline: 2px solid var(--accent); outline-offset: 4px; }
